Kakarot
Kakarot

Reputation: 415

How to generate new Access token by using refresh token in power bi custom connector

I have created a custom connector that calls an API and generate report Now for that I need access token while connecting the connector first time we are able to generate the access token but once access token gets expired i want it should call refresh token and use the new access token from that.

The code contains the startlogin, FinishLogin and refreshtoken function


[Version = "2.0.0"]
section HelloWorld
;

[DataSource.Kind = "HelloWorld
", Publish = "HelloWorld
.Publish"]
// Define the shared navigation table to expose multiple functions
shared NavigationTable.Simple = () =>
    let
        // Define default values for the parameters (can be changed dynamically by the user)
        envelopeId = "1",
        // Example envelopeId (this can be dynamically set later)
        customerName = "2",
        // Example customerName (this can be dynamically set later)
        // Define the available functions in the navigation table
        objects = #table(
            {"Name", "Key", "Data", "ItemKind", "ItemName", "IsLeaf"},
            {
                {
                    "DSDB",
                    "Customers",
                    HelloWorld
.FunctionCallThatReturnsATable(envelopeId),
                    "Table",
                    "Table",
                    true
                }
                // ,
                // {"DSDB", "Products", HelloWorld
.FunctionCallWithFilter(customerName), "Table", "Table", true},
                // {"DSDB", "Test", HelloWorld
.Test(), "Table", "Table", true}
            }
        ),
        // Convert to a navigation table
        NavTable = Table.ToNavigationTable(objects, {"Key"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
    in
        NavTable;

// Function to get the base URL based on the selected environment
shared GetBaseUrl = (environment as text) =>
    let
        envBaseUrls = [
            Dev = "http://localhost:5275/odata",
            // Dev base URL
            Int = "https://staging.docusign.com/odata",
            // Staging base URL
            Prod = "https://api.docusign.com/odata"
            // Production base URL
        ],
        // Look up the base URL for the selected environment
        baseUrl = Record.FieldOrDefault(envBaseUrls, environment, "http://localhost:5275/odata")
        // Default to Dev if not found
    in
        baseUrl;

shared HelloWorld
.FunctionCallThatReturnsATable = (customerId as text) =>
    let
        // Get the access token from Power BI's OAuth flow
        token = Extension.CurrentCredential()[access_token],
        // Construct the API URL (you can replace this with the actual endpoint)
        url = "http://localhost:5275/odata/Customers",
        // Example URL
        // Fetch the data from the API with the dynamic envelopeId
        source = Json.Document(Web.Contents(url, [
            Headers = [
                #"Authorization" = "Bearer " & token
                // Add the token in the Authorization header
            ]
        ]))[value],
        // Convert the API response to a table
        asTable = Table.FromRecords(source)
    in
        asTable;


Table.ToNavigationTable = (
    table as table,
    keyColumns as list,
    nameColumn as text,
    dataColumn as text,
    itemKindColumn as text,
    itemNameColumn as text,
    isLeafColumn as text
) as table =>
    let
        tableType = Value.Type(table),
        newTableType = Type.AddTableKey(tableType, keyColumns, true) meta [
            NavigationTable.NameColumn = nameColumn,
            NavigationTable.DataColumn = dataColumn,
            NavigationTable.ItemKindColumn = itemKindColumn,
            Preview.DelayColumn = itemNameColumn,
            NavigationTable.IsLeafColumn = isLeafColumn
        ],
        navigationTable = Value.ReplaceType(table, newTableType)
    in
        navigationTable;

// OAuth2 Authentication setup
redirect_uri = "https://oauth.powerbi.com/views/oauthredirect.html";
state = "test";
// This can be a random string to track the state
client_id = "ec1f622b-a49d-44a7-a821-6ac9e0715d4a";
windowHeight = 800;
windowWidth = 800;

// The sample provides two code_challenge_method examples: "plain" and "S256".
code_challenge_method = "S256";

// Start OAuth login flow
StartLogin = (resourceUrl, state, display) =>
    let
        code_verifier = Text.NewGuid() & Text.NewGuid(),
        code_challenge =
            if (code_challenge_method = "plain") then
                code_verifier
            else if (code_challenge_method = "S256") then
                Base64Url.Encode(Crypto.CreateHash(CryptoAlgorithm.SHA256, Text.ToBinary(code_verifier)))
            else
                error "Unexpected code_challenge_method",
        AuthorizeUrl = "https://account-tk1.tk.docusign.dev/oauth/auth?"
            & Uri.BuildQueryString(
                [
                    response_type = "code",
                    client_id = client_id,
                    scope = "lens_api signature extended impersonation user_read",
                    state = state,
                    code_challenge_method = code_challenge_method,
                    code_challenge = code_challenge,
                    redirect_uri = redirect_uri
                ]
            ),
        windowHeight = 700,
        windowWidth = 700
    in
        [
            LoginUri = AuthorizeUrl,
            CallbackUri = redirect_uri,
            WindowHeight = windowHeight,
            WindowWidth = windowWidth,
            Context = code_verifier
        ];

// Finalize OAuth login and obtain the token
FinishLogin = (context, callbackUri, state) =>
    let
        Parts = Uri.Parts(callbackUri)[Query]
    in
        TokenMethod("authorization_code", Parts[code], context);

Refresh = (resourceUrl, refresh_token) => TokenMethod("refresh_token", refresh_token, "dan");

// Token exchange method
TokenMethod = (grant_type, code, code_verifier) =>
    let
        // Define the base URL for the token request
        tokenUrl = "https://account-tk1.tk.docusign.dev/oauth/token",
        // Construct the body based on the grant type
        body =
            if grant_type = "authorization_code" then
                Uri.BuildQueryString(
                    [
                        grant_type = "authorization_code",
                        code = code,
                        code_verifier = code_verifier,
                        client_id = client_id
                    ]
                )
            else if grant_type = "refresh_token" then
                Uri.BuildQueryString([
                    grant_type = "refresh_token",
                    refresh_token = code,
                    client_id = client_id
                ])
            else
                error "Unsupported grant_type",
        // Send the token request
        Response = Web.Contents(
            tokenUrl,
            [
                Content = Text.ToBinary(body),
                Headers = [
                    #"Content-type" = "application/x-www-form-urlencoded",
                    #"Accept" = "application/json"
                ]
            ]
        ),
        // Parse the JSON response
        Parts = Json.Document(Response),
        // Extract the access token, refresh token, and expiration info
        accessToken = Parts[access_token],
        refreshToken = if Record.HasFields(Parts, "refresh_token") then Parts[refresh_token] else null,
        // Handle the case where no refresh token is provided
        expiresIn = Parts[expires_in],
        // Calculate the expiration date for the access token
        expiresAt = DateTime.LocalNow() + #duration(0, 0, 0, expiresIn / 60),
        // Return the access token, refresh token, and expiration time
        result = [
            access_token = accessToken,
            refresh_token = refreshToken,
            expires_at = expiresAt
        ]
    in
        result;

// Data Source Kind description (authentication)
HelloWorld
 = [
    TestConnection = (dataSourcePath) => {"HelloWorld
.ODataFeed"},
    Authentication = [
        OAuth2 = [
            StartLogin = StartLogin,
            FinishLogin = FinishLogin,
            Refresh = Refresh
        ]
    ],
    Label = Extension.LoadString("DataSourceLabel")
];


// Icon definitions for the connector UI
HelloWorld
.Icons = [
    Icon16 = {
        Extension.Contents("HelloWorld
16.png"),
        Extension.Contents("HelloWorld
20.png"),
        Extension.Contents("HelloWorld
24.png"),
        Extension.Contents("HelloWorld
32.png")
    },
    Icon32 = {
        Extension.Contents("HelloWorld
32.png"),
        Extension.Contents("HelloWorld
40.png"),
        Extension.Contents("HelloWorld
48.png"),
        Extension.Contents("HelloWorld
64.png")
    }
];

Base64Url.Encode = (s) =>
    Text.Replace(
        Text.Replace(Text.BeforeDelimiter(Binary.ToText(s, BinaryEncoding.Base64), "="), "+", "-"), "/", "_"
    );

Upvotes: 1

Views: 50

Answers (0)

Related Questions