Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Find everything you need to get certified on Fabric—skills challenges, live sessions, exam prep, role guidance, and more. Get started

Reply
CaseWareUK
New Member

Power Query custom Data connector authentication issues.

Hi,

 

I have been working on a custom data connector and have ran into some issues with authentication.

The first time the connector is run on a machine it always works correctly, but if it is run a second time users get the error "We couldn't authenticate with the credentials provided, please try again." and the only way to resolve this seems to be clearing the cache data in Power Bi.

The connector uses the UsernamePassword authenication method, It also requires a few paramters, all of which I have excluded from the data source path to ensure that is not the issue. It then runs some queries on a REST api to get the required data.

I have included the code for the first draft of the connector below, any help would be much appreciated.


// This file contains your Data Connector logic
section Custom_Connector_Connector;
//Current error "We couldn't authenticate with the credentials provided, please try again."
client_id = Text.FromBinary(Extension.Contents("client_id"));

//Default Entry point for the extension.
//The user will be provided with a input field for firm name, and a date range to filter the results by.
[DataSource.Kind="Custom_Connector_Connector", Publish="Custom_Connector_Connector.Publish"]
shared Custom_Connector_Connector.Contents = Value.ReplaceType(Custom_Connector_ConnectorImpl, Custom_Connector_ConnectorType);

Custom_Connector_ConnectorType = type function (
    #"Firm Name" as (type text meta [
        DataSource.Path = false
    ]),
    From as (type date meta [
        DataSource.Path = false
    ]),
    To as (type date meta [
        DataSource.Path = false
    ]))
    as table;

Custom_Connector_ConnectorImpl = (#"Firm Name" as text, From as date, To as date) => //, From as datetime, To as datetime
    let
        //We log the user in via the api and get the access token to run our queries.
        AuthData = TokenMethod(),
        //We then run a predefined query to get all of the call data for the specified firm between the specified dates.
        AllData = GetAllData(AuthData[access_token], #"Firm Name",From, To),
        //We transform the simple Table of json data into the structure we want.
        TransformedData = TransformTable(AllData)
    in
        //Return the final table of data.
        TransformedData;

// Data Source Kind description
Custom_Connector_Connector = [
    Authentication = [
        UsernamePassword = []
    ],
    Label = Extension.LoadString("DataSourceLabel")
];

// Data Source UI publishing description
Custom_Connector_Connector.Publish = [
    Beta = true,
    Category = "Online Services",
    ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
    LearnMoreUrl = "https://powerbi.microsoft.com/",
    SourceImage = Custom_Connector_Connector.Icons,
    SourceTypeImage = Custom_Connector_Connector.Icons
];

Custom_Connector_Connector.Icons = [
    Icon16 = { Extension.Contents("Custom_Connector_Connector16.png"), Extension.Contents("Custom_Connector_Connector20.png"), Extension.Contents("Custom_Connector_Connector24.png"), Extension.Contents("Custom_Connector_Connector32.png") },
    Icon32 = { Extension.Contents("Custom_Connector_Connector32.png"), Extension.Contents("Custom_Connector_Connector40.png"), Extension.Contents("Custom_Connector_Connector48.png"), Extension.Contents("Custom_Connector_Connector64.png") }
];

/*
    TokenMethod 

    Returns: 
        Json Document.
    Desc: 
        Method makes a POST request to API to log the user account in and get an access token.
        Manual credentials must be true here or the Power Query API will automatically add an authorization header that will break the request. 
*/
TokenMethod = () =>
    let
        Response = Web.Contents("https://some.api.com", [
            IsRetry = true,
            ManualCredentials = true,
            Headers=[#"Content-Type" = "application/x-www-form-urlencoded", #"Accept" = "*/*"],
            RelativePath = "/Production/web/oauth/login",
            Content = Text.ToBinary(Uri.BuildQueryString([ 
                client_id = client_id, 
                grant_type = "password",
                scope= "session-type:Analyst",
                password = Extension.CurrentCredential()[Password],
                username = Extension.CurrentCredential()[Username]
                ]))]),
                
        Part = Json.Document(Response)
    in
        Part;

/*
    GetAllData 

    Returns: 
        Table.
    Desc: 
        Function gets all of the paginated data from the API. 
*/ 
GetAllData = (accessToken, FirmName, From, To) => 
    let 
        FirstResponse = QueryFirstPage(accessToken, FirmName, From, To),
        //We will use count to determine how many requests are needed. 
        //We default to 100 entries per page.
        TotalCount = FirstResponse[#"__count"],
        FirstPage = Table.FromRecords({FirstResponse}),
        AllData = if (TotalCount > 100) then  QueryPage(accessToken,FirmName, From, To,TotalCount, FirstPage, 100) else FirstPage
    in 
        AllData;

QueryFirstPage = (accessToken, FirmName, From, To) => 
    let
        DateFrom = Text.From(Date.Year(From))&"-"&Text.PadStart(Text.From(Date.Month(From)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(From)),2,"0"),
        DateTo = Text.From(Date.Year(To))&"-"&Text.PadStart(Text.From(Date.Month(To)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(To)),2,"0"),
        uri = Uri.Parts("https://some.api.com?"&Uri.BuildQueryString([
                #"$select" = "Ref,CreatedDate,Priority.Name,User.Name,Organization.Name", 
                #"$filter" = "CreatedDate >= @DateTime("&DateFrom&"T00:00:00.000Z) && CreatedDate < @DateTime("&DateTo&"T00:00:00.000Z) && (Organization.Name="""&FirmName&""" || Organization.Parent.Name="""&FirmName&""")",
                #"$top"= "100",
                #"$inlinecount" = "true"
              ])),
        Response = Web.Contents("https://some.api.com", [
              ManualCredentials = true,
              Headers=[#"Accept" = "*/*", #"Authorization" = "Bearer " & accessToken],
              Query = uri[Query],
              RelativePath = "/Production/api/v2/call"
            ]),        
        Page = Json.Document(Response)
    in
        Page;
        
QueryPage = (accessToken, FirmName, From, To, count, previousTable, skip) => 
    let
        DateFrom = Text.From(Date.Year(From))&"-"&Text.PadStart(Text.From(Date.Month(From)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(From)),2,"0"),
        DateTo = Text.From(Date.Year(To))&"-"&Text.PadStart(Text.From(Date.Month(To)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(To)),2,"0"),
        uri = Uri.Parts("https://some.api.com?"&Uri.BuildQueryString([
                #"$select" = "Ref,CreatedDate,Priority.Name,User.Name,Organization.Name", 
                #"$filter" = "CreatedDate >= @DateTime("&DateFrom&"T00:00:00.000Z) && CreatedDate < @DateTime("&DateTo&"T00:00:00.000Z) && (Organization.Name="""&FirmName&""" || Organization.Parent.Name="""&FirmName&""")",
                #"$top"= "100",
                #"$skip" = Text.From(skip)
            ])),
        Response = Web.Contents("https://some.api.com", [
            ManualCredentials = true,
            Headers=[#"Accept" = "*/*", #"Authorization" = "Bearer " & accessToken],
            Query = uri[Query],
            RelativePath = "/Production/api/v2/call"
            ]),    
        Page = Json.Document(Response),
        CurrentTable = Table.FromRecords({Page}),
        NextTable = Table.Combine({
            previousTable,
            CurrentTable
        })
    in
        if (count > skip + 100) then
            QueryPage(accessToken,FirmName,From, To, count, NextTable, skip + 100)
        else 
            NextTable;

TransformTable = (json) =>
    let
        #"Expanded results" = Table.ExpandListColumn(json, "results")
        // Do some transformations here
    in
        #"results";

3 REPLIES 3
stenford23
Helper I
Helper I


@CaseWareUK wrote:

Hi,

 

I have been working on a custom data connector and have ran into some issues with authentication.

The first time the connector is run on a machine it always works correctly, but if it is run a second time users get the error "We couldn't authenticate with the credentials provided, please try again." and the only way to resolve this seems to be clearing the cache data in Power Bi.

The connector uses the UsernamePassword authenication method, It also requires a few paramters, all of which I have excluded from the data source path to ensure that is not the issue. It then runs some queries on a REST api to get the required data.

I have included the code for the first draft of the connector below, any help would be much appreciated.


// This file contains your Data Connector logic
section Custom_Connector_Connector;
//Current error "We couldn't authenticate with the credentials provided, please try again."
client_id = Text.FromBinary(Extension.Contents("client_id"));

//Default Entry point for the extension.
//The user will be provided with a input field for firm name, and a date range to filter the results by.
[DataSource.Kind="Custom_Connector_Connector", Publish="Custom_Connector_Connector.Publish"]
shared Custom_Connector_Connector.Contents = Value.ReplaceType(Custom_Connector_ConnectorImpl, Custom_Connector_ConnectorType);

Custom_Connector_ConnectorType = type function (
    #"Firm Name" as (type text meta [
        DataSource.Path = false
    ]),
    From as (type date meta [
        DataSource.Path = false
    ]),
    To as (type date meta [
        DataSource.Path = false
    ]))
    as table;

Custom_Connector_ConnectorImpl = (#"Firm Name" as text, From as date, To as date) => //, From as datetime, To as datetime
    let

Cred = Extension.CurrentCredential(),
//We log the user in via the api and get the access token to run our queries. AuthData = TokenMethod(Cred[Username], Cred[Password]), //We then run a predefined query to get all of the call data for the specified firm between the specified dates. AllData = GetAllData(AuthData[access_token], #"Firm Name",From, To), //We transform the simple Table of json data into the structure we want. TransformedData = TransformTable(AllData) in //Return the final table of data. TransformedData; // Data Source Kind description Custom_Connector_Connector = [ Authentication = [ UsernamePassword = [] ], Label = Extension.LoadString("DataSourceLabel") ]; // Data Source UI publishing description Custom_Connector_Connector.Publish = [ Beta = true, Category = "Online Services", ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") }, LearnMoreUrl = "https://powerbi.microsoft.com/", SourceImage = Custom_Connector_Connector.Icons, SourceTypeImage = Custom_Connector_Connector.Icons ]; Custom_Connector_Connector.Icons = [ Icon16 = { Extension.Contents("Custom_Connector_Connector16.png"), Extension.Contents("Custom_Connector_Connector20.png"), Extension.Contents("Custom_Connector_Connector24.png"), Extension.Contents("Custom_Connector_Connector32.png") }, Icon32 = { Extension.Contents("Custom_Connector_Connector32.png"), Extension.Contents("Custom_Connector_Connector40.png"), Extension.Contents("Custom_Connector_Connector48.png"), Extension.Contents("Custom_Connector_Connector64.png") } ]; /* TokenMethod Returns: Json Document. Desc: Method makes a POST request to API to log the user account in and get an access token. Manual credentials must be true here or the Power Query API will automatically add an authorization header that will break the request. */ TokenMethod = (username as text, password as text) => let Response = Web.Contents("https://some.api.com", [ IsRetry = true, ManualCredentials = true, Headers=[#"Content-Type" = "application/x-www-form-urlencoded", #"Accept" = "*/*"], RelativePath = "/Production/web/oauth/login", Content = Text.ToBinary(Uri.BuildQueryString([ client_id = client_id, grant_type = "password", scope= "session-type:Analyst", password = password, username = username ]))]), Part = Json.Document(Response) in Part; /* GetAllData Returns: Table. Desc: Function gets all of the paginated data from the API. */ GetAllData = (accessToken, FirmName, From, To) => let FirstResponse = QueryFirstPage(accessToken, FirmName, From, To), //We will use count to determine how many requests are needed. //We default to 100 entries per page. TotalCount = FirstResponse[#"__count"], FirstPage = Table.FromRecords({FirstResponse}), AllData = if (TotalCount > 100) then QueryPage(accessToken,FirmName, From, To,TotalCount, FirstPage, 100) else FirstPage in AllData; QueryFirstPage = (accessToken, FirmName, From, To) => let DateFrom = Text.From(Date.Year(From))&"-"&Text.PadStart(Text.From(Date.Month(From)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(From)),2,"0"), DateTo = Text.From(Date.Year(To))&"-"&Text.PadStart(Text.From(Date.Month(To)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(To)),2,"0"), uri = Uri.Parts("https://some.api.com?"&Uri.BuildQueryString([ #"$select" = "Ref,CreatedDate,Priority.Name,User.Name,Organization.Name", #"$filter" = "CreatedDate >= @DateTime("&DateFrom&"T00:00:00.000Z) && CreatedDate < @DateTime("&DateTo&"T00:00:00.000Z) && (Organization.Name="""&FirmName&""" || Organization.Parent.Name="""&FirmName&""")", #"$top"= "100", #"$inlinecount" = "true" ])), Response = Web.Contents("https://some.api.com", [ ManualCredentials = true, Headers=[#"Accept" = "*/*", #"Authorization" = "Bearer " & accessToken], Query = uri[Query], RelativePath = "/Production/api/v2/call" ]), Page = Json.Document(Response) in Page; QueryPage = (accessToken, FirmName, From, To, count, previousTable, skip) => let DateFrom = Text.From(Date.Year(From))&"-"&Text.PadStart(Text.From(Date.Month(From)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(From)),2,"0"), DateTo = Text.From(Date.Year(To))&"-"&Text.PadStart(Text.From(Date.Month(To)),2,"0")&"-"&Text.PadStart(Text.From(Date.Day(To)),2,"0"), uri = Uri.Parts("https://some.api.com?"&Uri.BuildQueryString([ #"$select" = "Ref,CreatedDate,Priority.Name,User.Name,Organization.Name", #"$filter" = "CreatedDate >= @DateTime("&DateFrom&"T00:00:00.000Z) && CreatedDate < @DateTime("&DateTo&"T00:00:00.000Z) && (Organization.Name="""&FirmName&""" || Organization.Parent.Name="""&FirmName&""")", #"$top"= "100", #"$skip" = Text.From(skip) ])), Response = Web.Contents("https://some.api.com", [ ManualCredentials = true, Headers=[#"Accept" = "*/*", #"Authorization" = "Bearer " & accessToken], Query = uri[Query], RelativePath = "/Production/api/v2/call" ]), Page = Json.Document(Response), CurrentTable = Table.FromRecords({Page}), NextTable = Table.Combine({ previousTable, CurrentTable }) in if (count > skip + 100) then QueryPage(accessToken,FirmName,From, To, count, NextTable, skip + 100) else NextTable; TransformTable = (json) => let #"Expanded results" = Table.ExpandListColumn(json, "results") // Do some transformations here in #"results";

@CaseWareUK modification in green. in this way it should work

 

 

v-angzheng-msft
Community Support
Community Support

Hi, @CaseWareUK 

The connector works correctly the first time, which indicates that the connector code can work for the user.

Try the following steps for users:

This should be a permission/authentication error. When your user connect to his data source in an unauthorized account, the corresponding credential would be created. So he need to change credential,  go to “File –> Options and Settings –> Data Source Settings”, under “Global permissions”, choose to delete the old data source connections permission  and click on Edit permission in Power BI Desktop. Then try to connect to it again and check if it is successful.

 

 

Best Regards,
Community Support Team _ Zeon Zheng


If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

Hi Zeon Zheng, 

Unfortunately we have already tried this solution and it did not seem to solve the issue for us. 

 

Kind regards, Joe.

Thanks, 

Helpful resources

Announcements
Europe Fabric Conference

Europe’s largest Microsoft Fabric Community Conference

Join the community in Stockholm for expert Microsoft Fabric learning including a very exciting keynote from Arun Ulag, Corporate Vice President, Azure Data.

Power BI Carousel June 2024

Power BI Monthly Update - June 2024

Check out the June 2024 Power BI update to learn about new features.

RTI Forums Carousel3

New forum boards available in Real-Time Intelligence.

Ask questions in Eventhouse and KQL, Eventstream, and Reflex.

Top Solution Authors
Top Kudoed Authors