Azure B2C Refresh token for 3rd party Api Custom Policy

Sam Guisson 31 Reputation points
2022-01-08T14:54:51.977+00:00

I'm trying to to refresh an access token received through the idp pass through method.
After an hour the access_token isn't valid anymore and I can't seem to find a way to refresh it.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/idp-pass-through-user-flow?pivots=b2c-custom-policy

My claims provider looks like this :

 <ClaimsProvider>  
      <Domain>commonaad</Domain>  
      <DisplayName>Common AAD</DisplayName>  
      <TechnicalProfiles>  
        <TechnicalProfile Id="AADCommon-OpenIdConnect">  
          <DisplayName>Multi-Tenant AAD</DisplayName>  
          <Description>Login with your Contoso account</Description>  
          <Protocol Name="OpenIdConnect" />  
  <OutputTokenFormat>JWT</OutputTokenFormat>  
          <Metadata>  
            <Item Key="METADATA">https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration</Item>  
            <!-- Update the Client ID below to the Application ID -->  
            <Item Key="client_id">xxxxxxxxxxxxxxxxxxxxxxxxx</Item>  
            <Item Key="response_types">code</Item>  
            <Item Key="scope">openid profile https://xxxxxxxxxxxxx.onmicrosoft.com/sfc-cloud/user_impersonation</Item>  
            <Item Key="response_mode">form_post</Item>  
            <Item Key="HttpBinding">POST</Item>  
            <Item Key="UsePolicyInRedirectUri">false</Item>  
            <Item Key="DiscoverMetadataByTokenIssuer">true</Item>  
            <Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/xxxxxxxxxxxxxxxxxxxx, https://login.microsoftonline.com/xxxxxxxxxxxxxxxxxxxxx</Item>  
            <!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->  
          </Metadata>  
          <CryptographicKeys>  
            <Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret" />  
          </CryptographicKeys>  
          <OutputClaims>  
            <OutputClaim ClaimTypeReferenceId="identityProviderAccessToken" PartnerClaimType="{oauth2:access_token}" />  
            <OutputClaim ClaimTypeReferenceId="ms_refresh_token" PartnerClaimType="{oauth2:refresh_token}" DefaultValue="none"/>  
            <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" />  
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />  
            <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />  
            <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />  
            <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid" />  
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />  
            <OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />  
            <OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="preferred_username" />  
          </OutputClaims>  
          <OutputClaimsTransformations>  
            <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />  
            <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />  
            <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />  
            <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />  
          </OutputClaimsTransformations>  
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />  
        </TechnicalProfile>  
      </TechnicalProfiles>  
    </ClaimsProvider>  
  

From other posts i've found out that OpenIdConnect doesn't support a refresh token. So this way I will never be able to refresh the acces_token?
I've tried changing it to OAuth2 like another post suggested.

<TechnicalProfile Id="AADCommon-oauth2">  
 <DisplayName>Company Azure AD</DisplayName>  
 <Description>Login with your Company Azure AD</Description>  
 <Protocol Name="OAuth2"/>  
 <OutputTokenFormat>JWT</OutputTokenFormat>  
 <Metadata>  
 <Item Key="AccessTokenEndpoint">https://login.microsoftonline.com/common/oauth2/v2.0/token</Item>  
 <Item Key="authorization_endpoint">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</Item>  
 <Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>  
 <Item Key="ClaimsEndpoint">https://graph.microsoft.com/v1.0/me</Item>  
 <Item Key="client_id">xxxxxxxxxxxxxxxxxxxxx</Item>  
 <Item Key="IdTokenAudience">xxxxxxxxxxxxxxxxxx</Item>  
 <Item Key="DiscoverMetadataByTokenIssuer">true</Item>  
 <Item Key="HttpBinding">POST</Item>  
 <Item Key="response_types">code</Item>  
 <Item Key="scope">openid user.read offline_access https://xxxxxxxxxxxxxxxxxxxxx/sfc-cloud/user_impersonation</Item>  
 <Item Key="UsePolicyInRedirectUri">false</Item>  
 <Item Key="ValidTokenIssuerPrefixes">https://sts.windows.net/</Item>  
 </Metadata>  
 <CryptographicKeys>  
 <Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret"/>  
 </CryptographicKeys>  
 <OutputClaims>  
 <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication"/>  
 <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName"/>  
 <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="id"/>  
 <!-- <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/> -->  
 <OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="userPrincipalName"/>  
 <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName"/>  
 <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="surname"/>  
 <OutputClaim ClaimTypeReferenceId="identityProviderAccessToken" PartnerClaimType="{oauth2:access_token}"/>  
 <OutputClaim ClaimTypeReferenceId="ms_refresh_token" PartnerClaimType="{oauth2:refresh_token}" DefaultValue="none"/>  
 </OutputClaims>  
 <OutputClaimsTransformations>  
 <OutputClaimsTransformation ReferenceId="CreateAzureADIdentityProvider"/>  
 <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>  
 <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>  
 <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>  
 <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>  
 </OutputClaimsTransformations>  
 <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>  
</TechnicalProfile>  

But here the access_token doesn't work. The scope "https://xxxxxxxxxxxxxxxxxxxxx/sfc-cloud/user_impersonation" that I need to access the api
just isn't in the access token i get back. Even when I do a silent request with specifically that scope to get the access token. it still doesn't work.
What am I missing here?

Microsoft Entra External ID
Microsoft Entra External ID
A modern identity solution for securing access to customer, citizen and partner-facing apps and services. It is the converged platform of Azure AD External Identities B2B and B2C. Replaces Azure Active Directory External Identities.
2,610 questions
{count} votes

Accepted answer
  1. AmanpreetSingh-MSFT 56,286 Reputation points
    2022-01-12T13:27:03.76+00:00

    @Sam Guisson • Yes, graph call is mandatory as it returns the user's ID and other required attributes to create a user account in the B2C directory.
    You can try using RESTful Technical Profile for this purpose. Below is an example of acquiring Access Token using client_credentials grant that you need to replace with refresh_token grant.

    <TechnicalProfile Id="REST-AcquireAccessToken">  
      <DisplayName></DisplayName>  
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />  
      <Metadata>  
        <Item Key="ServiceUrl">https://login.microsoftonline.com/your-tenant-name.onmicrosoft.com/oauth2/v2.0/token</Item>  
        <Item Key="AuthenticationType">Basic</Item>  
         <Item Key="SendClaimsIn">Form</Item>  
      </Metadata>  
      <CryptographicKeys>  
        <Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_SecureRESTClientId" />  
        <Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_SecureRESTClientSecret" />  
      </CryptographicKeys>  
      <InputClaims>  
        <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" AlwaysUseDefaultValue="true" />  
        <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://graph.microsoft.com/.default" AlwaysUseDefaultValue="true" />  
      </InputClaims>  
      <OutputClaims>  
        <OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />  
      </OutputClaims>  
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />  
    </TechnicalProfile>  
    

    -----------------------------------------------------------------------------------------------------------

    Please "Accept the answer" if the information helped you. This will help us and others in the community as well.

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Sam Guisson 31 Reputation points
    2022-01-17T09:59:27.837+00:00

    Hello, again. So I tried to modify it to a refresh_token grant and added it to my user journey :

     <TechnicalProfile Id="REST-AcquireAccessToken">
        <DisplayName></DisplayName>
        <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        <Metadata>
      <Item Key="ServiceUrl">https://login.microsoftonline.com/xxxx.onmicrosoft.com/oauth2/v2.0/token</Item>
      <Item Key="AuthenticationType">None</Item>
      <Item Key="SendClaimsIn">Form</Item>
        </Metadata>
      <CryptographicKeys>
     <Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret"/>
      </CryptographicKeys>
        <InputClaims>
      <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="refresh_token" AlwaysUseDefaultValue="true" />
      <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="4e1bb810-38fb-406d-a670-e64eb3853a1f" AlwaysUseDefaultValue="true" />
      <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://xxxx.onmicrosoft.com/sfc-cloud/user_impersonation" AlwaysUseDefaultValue="true" />
         <InputClaim ClaimTypeReferenceId="refresh_token" />
        </InputClaims>
        <OutputClaims>
         <OutputClaim ClaimTypeReferenceId="identityProviderAccessToken" PartnerClaimType="access_token" />
        </OutputClaims>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
    

    but I'm keep getting bad request :

    Exception Message:The claims exchange 'REST-AcquireAccessToken' specified in step '3' returned HTTP error response with Code 'BadRequest' and Reason 'Bad Request'., Exception Type:InvalidResponseException, CorrelationID:569c1d67-4894-4292-9fa8-fc644b4790be

    My statebag at this point

    {
        "Kind": "HandlerResult",
        "Content": {
          "Result": true,
          "Statebag": {
            "Complex-CLMS": {
              "authenticationSource": "socialIdpAuthentication",
              "displayName": "Sam Guisson",
              "issuerUserId": "8cab4eb3-239f-480b-88f7-ac9933491b6d",
              "userPrincipalName": "cpim_e30ca072-c979-4e9f-a58f-2a833f8dc1fd@xxxxxb2cpoc.onmicrosoft.com",
              "givenName": "Sam",
              "surname": "Guisson",
              "refresh_token": "{\r\n  \"r\": \"0.AQsAEguPiIzmmkacZFWgNO2TARC4G077OG1ApnDmTrOFOh8LAEE.AgABAAAAAAD...aQp_dbIbKrZe-YGQECEEuXnPNQKXId0CvOO-P3BOs\",\r\n  \"d\": \"oauth2commonaad\"\r\n}",
              "identityProvider": "clientidp.eu",
              "upnUserName": "e30ca072-c979-4e9f-a58f-2a833f8dc1fd",
              "alternativeSecurityId": "{\"type\":6,\"identityProvider\":\"clientidp.eu\",\"key\":\"OGNhYjRlYjMtMjM5Zi00ODBiLTg4ZjctYWM5OTMzNDkxYjZk\"}",
              "sub": "Not supported currently. Use oid claim.",
              "grant_type": "refresh_token",
              "client_id": "4e1bb810-38fb-406d-a670-e64eb3853a1f",
              "scope": "https://xxxx.onmicrosoft.com/sfc-cloud/user_impersonation"
            }
          }
        }
     }
    

    It seems to be working now. I had to cleanup the refresh token since it was in json format.
    thanks a lot!

    1 person found this answer helpful.