Got a somewhat convoluted and outdated workflow that we're trying to "somewhat" modernize. We are using a very old set of code (.NET 4.6.2), and are migrating an Azure AD B2C ROPC flow to an Authorization Code flow. We have the basic setup working - we capture the auth endpoint using IAppBuilder.Map
, and then use IAppBuilder.Run
to call Authentication.Challenge
with the specified auth type.
In terms of the auth provider registration, we use UseOpenIdConnectAuthentication
, with the following options:
AuthenticationType = AuthenticationType.Storefront,
ClientId = clientId,
Authority = authority,
SignInAsAuthenticationType = AuthenticationType.Storefront,
Scope = OpenIdConnectScopes.OpenId,
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
PostLogoutRedirectUri = "/",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
NameClaimType = "name",
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
return Task.CompletedTask;
},
RedirectToIdentityProvider = RedirectToSameDomain,
SecurityTokenValidated = OnOrgUserSecurityTokenValidated,
AuthorizationCodeReceived = OnOrgAuthorizationCodeReceived
}
This works perfectly for getting the browser to redirect the user to Azure AD B2C, and then capturing the response with the SecurityTokenValidated
callback. The problem is that in that response, the refresh token is always missing. We've tried several different places:
AuthenticationTicket.Properties.AllowRefresh
is always false
, despite setting AuthenticationProperties.AllowRefresh
to true
during the Authentication.Challenge
stepProtocolMessage.AccessToken
contains a valid access token, however ProtocolMessage.RefreshToken
is always null
SecurityTokenValidated
or at AuthorizationCodeReceived
On top of all of the above, there's one more question which we're unsure about. Currently we use ROPC to refresh the access token. Will that work even if we use the Authorization Code flow for signing in?
Any suggestions would be appreciated. Thanks!
EDIT
Going to mark the answer from Rukmini (https://stackoverflow.com/a/76578895/1289046) as correct, but I wanted to elaborate a bit on the specific steps I needed to take, to get this working.
First things first - in terms of the setup info for what gets sent over to Azure AD B2C, the first authorize
call is sent using scope=openid
and response_type=code id_token
. I then hook into the SecurityTokenValidated
message that Azure AD B2C sends back when authentication has occurred successfully.
In there, I modified the overall flow significantly. From the response I get from Azure AD B2C, I take only the ProtocolMessage.Code
value, and I use that to make another call to Azure AD B2C. This time, though, I call it using grant_type=authorization_code
and I set the code
parameter to the aforementioned ProtocolMessage.Code
value. I make this call using a client_id
and client_secret
registered in Azure AD B2C.
The response of this second call properly contains the refresh_token
, alongside the id_token
and an expires_in
value for both tokens.
Last, but not least, I rewired the refresh token behaviour - as long as the refresh_token
hasn't expired, I use it to get a new id_token
if it has expired or will soon do so. If the refresh_token
has expired, I log the user out.
I created an Azure AD B2C Application and granted API permissions like below:
Now, I generated access token using ROPC flow using below parameters via Postman:
https://b2ctenant.b2clogin.com/b2ctenant.onmicrosoft.com/<policy-name>/oauth2/v2.0/token
client_id:ClientID
scope:openid
username:[email protected]
password:*****
grant_type:password
client_secret:ClientSecret
Note that: To generate refresh token, the
offline_access
scope is required.
To generate the refresh token, grant offline_access
API permission:
Now while generating the token, pass offline_access
scope like below:
scope:openid offline_access
To get refresh token, modify the code like below:
AuthenticationType = AuthenticationType.Storefront,
ClientId = clientId,
Authority = authority, SignInAsAuthenticationType = AuthenticationType.Storefront,
Scope = OpenIdConnectScopes.OpenId + "offline_access",
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
On top of all of the above, there's one more question which we're unsure about. Currently we use ROPC to refresh the access token. Will that work even if we use the Authorization Code flow for signing in?
https://b2ctenant.b2clogin.com/b2ctenant.onmicrosoft.com/B2C_1_Signinsignup/oauth2/v2.0/token
client_id:xxxx
grant_type:authorization_code
scope:openid offline_access
code:code
redirect_uri:https://jwt.ms
client_secret:ClientSecret
Yes, you can use ROPC to refresh the access token even if Authorization Code flow is used for signing in.
I am able to refresh the access token generated by authorization code and using refresh token of ROPC: