Forum Discussion

Anonymous's avatar
Anonymous
Not applicable
6 years ago

Custom Data Connector issue with authentication

Hi

 

I've created a custom data connector using Oauth flow. I'm receiving an error once I select my account to login. Error description below:

 

"[Invalid Grant] AADB2C90090: The provided JWE is not a valid 5 segment token."

 

I've tried searching in google and found a few posts but no concrete solution.

 

Can someone familiar advise what's wrong here and how can I solve it?

 

Thanks
AfsarP1

4 Replies

    • Anonymous's avatar
      Anonymous
      Not applicable

      Hi Maggie

       

      I'm building a code to connect to our cloud. I've looked at Jussi's code and also codes from Matt Mason on Github(MyGraph in particular) and built the below code:

       

      // This file contains your Data Connector logic
      section System;

       

      client_id = Text.FromBinary(Extension.Contents("client_id.txt"));
      redirect_uri = "https://ourdomain/oauth2/authresp";
      token_uri = "https://ourdomain/oauth2/v2.0/token"; //Used to retrieve an access token
      authorize_uri = "https://ourdomain/oauth2/authorize";
      logout_uri = "https://login.microsoftonline.com/logout.srf";

      // Login modal window dimensions
      windowWidth = 720;
      windowHeight = 1024;

      // OAuth2 scope
      scope_prefix = "https://ourdomian.com";
      scopes = {
      "daily",
      "User.Read",
      "openid"
      //".Read.All",
      //"IdentityProvider.Read.All"
      //"client_id"

      //It can be delegated permission or app permission https://docs.microsoft.com/en-us/graph/auth/auth-concepts#microsoft-graph-permissions
      };

      [DataSource.Kind="System", Publish="System.Publish"]
      shared System.Contents = (url as text) =>

      let
      source = Json.Document(Web.Contents(url)) //If the data is returned from the datasource in JSON format

      in
      source;

      // Data Source Kind description
      System = [
      TestConnection = (dataSourcePath) => { "System.Contents", dataSourcePath },
      Authentication = [
      OAuth = [
      StartLogin=StartLogin,
      FinishLogin=FinishLogin,
      Refresh=Refresh,
      Logout=Logout
      ]
      ],

      Label = Extension.LoadString("DataSourceLabel")
      ];


      // Data Source UI publishing description
      System.Publish = [
      Beta = true,
      Category = "Other",
      ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
      LearnMoreUrl = "https://powerbi.microsoft.com/", //This can be replaced with our System URI. This is a help URI which gives more info about this connector.
      SourceImage = System.Icons,
      SourceTypeImage = System.Icons
      ];

      //Helper functions for OAuth2: StartLogin, FinishLogin, Refresh, Logout. Start Login definition:
      StartLogin = (resourceUrl, state, display) =>
      let
      authorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
      client_id = client_id,
      response_type = "code",
      redirect_uri = redirect_uri,
      state = state,
      scope = "offline_access " & GetScopeString(scopes, scope_prefix) // or GetScopeString(scopes, scope_prefix)
      //response_mode = "query",
      //login = "login"
      ])
      in
      [
      LoginUri = authorizeUrl,
      CallbackUri = redirect_uri,
      WindowHeight = 720,
      WindowWidth = 1024,
      Context = null
      ];

      //Function that extracts the access_token and other properties related to the OAuth flow.:
      FinishLogin = (context, callbackUri, state) =>
      let
      // parse the full callbackUri, and extract the Query string
      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;

      //Function that retrieves a new access token from a refresh token.
      //Refresh definition:
      Refresh = (resourceUrl, refresh_token) => TokenMethod("authorization_code", "code", refresh_token);

      //Function that invalidates the user's current access token:
      //Logout definition:
      Logout = (token) => logout_uri;

      //Below definition is to exchange an auth code for an access token:
      TokenMethod = (grantType, tokenField, code) =>
      let
      queryString = [
      client_id = client_id,
      grant_type = "authorization_code", //grantType, //"authorization_code", //This can also be grant_type
      scope = "offline_access " & GetScopeString(scopes, scope_prefix),//Optional
      redirect_uri = redirect_uri
      //client_id = client_id

      ],
      queryWithCode = Record.AddField(queryString, tokenField, code),

      tokenResponse = Web.Contents(token_uri, [
      Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
      Headers = [
      //#"Authorization" = "Basic XXXXXXXXXXX encoded client key and secrect",
      #"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;

      Value.IfNull = (a, b) => if a <> null then a else b;

      GetScopeString = (scopes as list, optional scopePrefix as text) as text =>
      let
      prefix = Value.IfNull(scopePrefix, ""),
      addPrefix = List.Transform(scopes, each prefix & _),
      asText = Text.Combine(addPrefix, " ")
      in
      asText;


      System.Icons = [
      Icon16 = { Extension.Contents("System16.png"), Extension.Contents("System20.png"), Extension.Contents("System24.png"), Extension.Contents("System32.png") },
      Icon32 = { Extension.Contents("System32.png"), Extension.Contents("System40.png"), Extension.Contents("System48.png"), Extension.Contents("System64.png") }
      ];

       

      I'm building this code without the need of Client Secret and I have a client_id.txt file which contains my clientid. I also have the API username and password but cannot figure out how to use it within this code. I tried using the UsernameLabel and PasswordLabel(https://docs.microsoft.com/en-us/power-query/handlingauthentication)l but the datasource doesn't accept this format.

       

      This is how we used it in our old powershell code:

       

      # Get the bearer token from AAD B2C using ROPC flow
      $bearerBody = @{
      client_id="xxxxxxxxxxxxxxx"
      scope="yyyyyyyyyyyyyyyyyyyyy"
      grant_type="password"
      response_type="token"
      username="abc123"
      password="**********"
      }

       

      How can modify my code to use the API username and password?

       

      Thanks 

      AfsarP1

    • Anonymous's avatar
      Anonymous
      Not applicable
      Hi Maggie
       
      Looks like my response was marked as SPAM. Resending:
       
      I'm building a connector that connects to our cloud environment. I've looked at Jussi's blog and also codes from Matt Mason(MyGraphmost) on Gihub to build below code:
       

      // This file contains your Data Connector logic
      section System;

       

      client_id = Text.FromBinary(Extension.Contents("client_id.txt"));
      redirect_uri = "https://ourdomain/oauth2/authresp";
      token_uri = "https://ourdomain/oauth2/v2.0/token"; //Used to retrieve an access token
      authorize_uri = "https://ourdomain/oauth2/authorize";
      logout_uri = "https://login.microsoftonline.com/logout.srf";

      // Login modal window dimensions
      windowWidth = 720;
      windowHeight = 1024;

      // OAuth2 scope
      scope_prefix = "https://ourdomian.com";
      scopes = {
      "daily",
      "User.Read",
      "openid"
      //".Read.All",
      //"IdentityProvider.Read.All"
      //"client_id"

      //It can be delegated permission or app permission https://docs.microsoft.com/en-us/graph/auth/auth-concepts#microsoft-graph-permissions
      };

      [DataSource.Kind="System", Publish="System.Publish"]
      shared System.Contents = (url as text) =>

      let
      source = Json.Document(Web.Contents(url)) //If the data is returned from the datasource in JSON format

      in
      source;

      // Data Source Kind description
      System = [
      TestConnection = (dataSourcePath) => { "System.Contents", dataSourcePath },
      Authentication = [
      OAuth = [
      StartLogin=StartLogin,
      FinishLogin=FinishLogin,
      Refresh=Refresh,
      Logout=Logout
      ]
      ],

      Label = Extension.LoadString("DataSourceLabel")
      ];


      // Data Source UI publishing description
      System.Publish = [
      Beta = true,
      Category = "Other",
      ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
      LearnMoreUrl = "https://powerbi.microsoft.com/", //This can be replaced with our System URI. This is a help URI which gives more info about this connector.
      SourceImage = System.Icons,
      SourceTypeImage = System.Icons
      ];

      //Helper functions for OAuth2: StartLogin, FinishLogin, Refresh, Logout. Start Login definition:
      StartLogin = (resourceUrl, state, display) =>
      let
      authorizeUrl = authorize_uri & "?" & Uri.BuildQueryString([
      client_id = client_id,
      response_type = "code",
      redirect_uri = redirect_uri,
      state = state,
      scope = "offline_access " & GetScopeString(scopes, scope_prefix) // or GetScopeString(scopes, scope_prefix)
      //response_mode = "query",
      //login = "login"
      ])
      in
      [
      LoginUri = authorizeUrl,
      CallbackUri = redirect_uri,
      WindowHeight = 720,
      WindowWidth = 1024,
      Context = null
      ];

      //Function that extracts the access_token and other properties related to the OAuth flow.:
      FinishLogin = (context, callbackUri, state) =>
      let
      // parse the full callbackUri, and extract the Query string
      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;

      //Function that retrieves a new access token from a refresh token.
      //Refresh definition:
      Refresh = (resourceUrl, refresh_token) => TokenMethod("authorization_code", "code", refresh_token);

      //Function that invalidates the user's current access token:
      //Logout definition:
      Logout = (token) => logout_uri;

      //Below definition is to exchange an auth code for an access token:
      TokenMethod = (grantType, tokenField, code) =>
      let
      queryString = [
      client_id = client_id,
      grant_type = "authorization_code", //grantType, //"authorization_code", //This can also be grant_type
      scope = "offline_access " & GetScopeString(scopes, scope_prefix),//Optional
      redirect_uri = redirect_uri
      //client_id = client_id

      ],
      queryWithCode = Record.AddField(queryString, tokenField, code),

      tokenResponse = Web.Contents(token_uri, [
      Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
      Headers = [
      //#"Authorization" = "Basic XXXXXXXXXXX encoded client key and secrect",
      #"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;

      Value.IfNull = (a, b) => if a <> null then a else b;

      GetScopeString = (scopes as list, optional scopePrefix as text) as text =>
      let
      prefix = Value.IfNull(scopePrefix, ""),
      addPrefix = List.Transform(scopes, each prefix & _),
      asText = Text.Combine(addPrefix, " ")
      in
      asText;


      System.Icons = [
      Icon16 = { Extension.Contents("System16.png"), Extension.Contents("System20.png"), Extension.Contents("System24.png"), Extension.Contents("System32.png") },
      Icon32 = { Extension.Contents("System32.png"), Extension.Contents("System40.png"), Extension.Contents("System48.png"), Extension.Contents("System64.png") }
      ];

       

      I'm building this code without the need of Client Secret and I have a client_id.txt file which contains my clientid. I also have the API username and password but cannot figure out how to use it within this code. I've tried using the UsernameLabel and PasswordLabel(https://docs.microsoft.com/en-us/power-query/handlingauthentication)l but the datasource doesn't accept this format.

       

      This is how we used it in our old powershell code:

       

      # Get the bearer token from AAD B2C using ROPC flow
      $bearerBody = @{
      client_id="xxxxxxxxxxxxxxx"
      scope="yyyyyyyyyyyyyyyyyyyyy"
      grant_type="password"
      response_type="token"
      username="abc123"
      password="**********"
      }

       
      I want to use the API username and password with the Oauth flow to successfully connect to our cloud environment. Appreciate if you could help. 
      • Anonymous's avatar
        Anonymous
        Not applicable

        Hi 

         

        Does anyone have a suggestion on how to solve this error?

         

        Appreciate your response. 

         

        Thanks

        AfsarP1