What application type should I choose for a Power BI custom data connector

Hi @jjkaandorp.

Roger. So the solution from a Power BI custom data connector oAuth implementation with PCKE flow looks like this. If you have any questions or comments, please let me know. Some comments:

  • Make sure your auth0 login page supports IE 11
  • You need offline enabled for the below to work

Helper variables

client_id = "<AUTH0 CLIENT ID>"; 
redirect_uri = "https://oauth.powerbi.com/views/oauthredirect.html";
authorize_uri = "https://<YOUR_AUTH_DOMAIN>.com/authorize";
token_uri = "https://<YOUR_AUTH_DOMAIN>.com/oauth/token";
windowWidth = 1200;
windowHeight = 1000;
codeVerifier = Text.NewGuid() & Text.NewGuid();

Start login function

StartLogin = (resourceUrl, state, display) =>
    let
        params = Json.Document(resourceUrl),
        codeChallenge = Base64UrlEncodeWithoutPadding(Crypto.CreateHash(CryptoAlgorithm.SHA256, Text.ToBinary(codeVerifier, TextEncoding.Ascii))),
        AuthorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
            client_id = client_id,
            scope = "openid offline_access",
            response_type = "code",
            state = state,
            redirect_uri = redirect_uri,
            code_challenge_method = "S256",
            code_challenge = codeChallenge
            
            
            ])
    in
        [
            LoginUri = AuthorizeUrl,
            CallbackUri = redirect_uri,
            WindowHeight = windowHeight,
            WindowWidth = windowWidth,
            Context = []
        ];

Refresh your token

Refresh = (resourceUrl, refresh_token) =>
    let
         result =  TokenMethod("refresh_token", "refresh_token", refresh_token)
    in
        result;

Finish login

FinishLogin = (context, callbackUri, state) =>
    let
        parts = Uri.Parts(callbackUri)[Query],
        // if the query string contains an "error" field, raise an error
        // otherwise call TokenMethod to exchange our code for an access_token
        result = if (Record.HasFields(parts, {"error", "error_description"})) then 
                    error Error.Record(parts[error], parts[error_description], parts)
                 else
                    TokenMethod("authorization_code", "code", parts[code])
    in
        result;

Helper

Base64UrlEncodeWithoutPadding = (hash as binary) as text =>
    let
        base64Encoded = Binary.ToText(hash, BinaryEncoding.Base64),
        base64UrlEncoded = Text.Replace(Text.Replace(base64Encoded, "+", "-"), "/", "_"),
        withoutPadding = Text.TrimEnd(base64UrlEncoded, "=")
    in 
        withoutPadding;

TokenMethod

TokenMethod = (grantType, tokenField, code) =>
    let
        queryString = [
            grant_type = grantType,
            redirect_uri = redirect_uri,
            client_id = client_id,
            code_verifier = codeVerifier
        ],
        // hans =  Diagnostics.Trace(TraceLevel.Information, "TokenMethod: " & code, code),
        queryWithCode = Record.AddField(queryString, tokenField, code),

        tokenResponse = Web.Contents(token_uri, [
            Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
            Headers = [
                #"Content-type" = "application/x-www-form-urlencoded",
                #"Accept" = "application/json"
            ],
            ManualStatusHandling = {400} 
        ]),
        body = Json.Document(tokenResponse),
        result = if (Record.HasFields(body, {"error", "error_description"})) then 
                    error Error.Record(body[error], body[error_description], body)
                 else
                    body
    in
        result;
1 Like