How to trade code for Access Token using owin - owin

I'm using Identity Server 4 V3 and a .Net Framework V4.6 with Owin client. I'm trying to implement the authroization_code flow, but cannot figure out how to trade the code supplied by Identity Server for an access and id token. How can I do this in the following event handler:
AuthorizationCodeReceived = async context =>
{
var code = context.Code;
//Now what?
return;
}

Seen in doc :
https://identityserver4.readthedocs.io/en/latest/endpoints/token.html?highlight=grant_type%3D%22code%22#example
POST /connect/token
client_id=client1&
client_secret=secret&
grant_type=authorization_code&
code=hdh922&
redirect_uri=https://myapp.com/callback

In my Configuration Method in Startup.cs I needed the following settings:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
...
ResponseType = "code",
RedeemCode = true,
...
});

Related

How to Get Azure Id Token after successful login in asp .net core application

I can see UserId token in Formdata in response of signinodc API. How can I get that token in asp .net core application?
Below code has written for authentication:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
In Configure method app.UseAuthentication(); app.UseAuthorization();
You can use get AccessToken from the API by using below code
public async Task<JArray> GetApiDataAsync()
{
try
{
var client = _clientFactory.CreateClient();
var scope = _configuration["CallApi:ScopeForAccessToken"];
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { scope });
...
...
}
}
Follow the steps using on the blog
Check here for more reference of the AccessToken

Microsoft.Identity.Web and ASP.NET Core SignalR JWT authentication

I am using ASP.NET Core to make a web application that also uses SignalR Core to provide real time functionality. I use Azure AD B2C for user management. I have successfully used Microsoft.Identity.Web (https://github.com/AzureAD/microsoft-identity-web) to secure my API endpoints using tokens generated by Azure AD B2C.
I would like to do the same for my SignalR Core hubs. The documentation reads to add the appropriate annotation to your hubs/methods, which I have done. SignalR's client side library adds the access token as a query parameter which must be extracted and added to the context manually in the configuration of your ASP.NET core application, like so.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
However, this seems to be incompatible with the configuration supplied by Microsoft.Identity.Web, here:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
How can I make SignalR work with Microsoft.Identity.Web?
That should do it:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(configuration);
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
Func<MessageReceivedContext, Task> existingOnMessageReceivedHandler = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await existingOnMessageReceivedHandler(context);
StringValues accessToken = context.Request.Query["access_token"];
PathString path = context.HttpContext.Request.Path;
// If the request is for our hub...
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
// Read the token out of the query string
context.Token = accessToken;
}
};
});
Instead of adding a JwtBearer, you can configure the JwtBearerOptions object this way.
Adapted from this documentation: https://github.com/AzureAD/microsoft-identity-web/wiki/customization
You can use Visual Studio to set up the SignalR connection, and then just add this line in startup.cs (VS might add it automatically)
services.AddSignalR().AddAzureSignalR();
This dev sample has SignalR set up, just the connection string is missing, but might give you an idea of what to do. Most of this was done automatically with VS. If you have issues setting it up, please open an issue in the repo. thanks.

Owin not authenticated when requesting access token

I'm using implicit grant type, and when I request "id_token token" as response type my HttpContext.Current.User is null after logging in leading me to believe something has gone wrong inside owin. If I just have "id_token" as response type its fine. Do I need to tell owin somewhere to get the access token?
For reference I'm using .Net Framework as my client and identityserver4.
To be able to get the token via browser you need to set AllowAccessTokensViaBrowser = true on client's config within IdentityServer:
new Client
{
...
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
...
},
and on MVC client's Startup, to you can add the access_token as a claim to user:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
...
ResponseType = "id_token token",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
return Task.FromResult(0);
}
}
});
I have the full working sample here

Convert authorization code to access_token in ASP.NET Core MVC to call a .NET framework API

I have an ASP.NET Core MVC application which calls a .NET framework API.
The MVC application uses the hybrid flow and gets the id_token and authorization code using the following code in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["AzureAD:Instance"] +
"/" + Configuration["AzureAD:TenantId"];
options.ClientId = Configuration["AzureAD:ClientId"];
options.Secret = Configuration["AzureAD:Secret"];
options.Callback = Configuration["AzureAD:Callback"];
options.ResponseType = "code id_token";
options.SaveTokens = true;
});
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "sign-in",
template: "signin-oidc")};
...
});
In the controller, when I check the HttpContext, I can get the id_token and authorization code. But I need to pass the access_token to the API.
How do I get the access_token from the authorization code?
Is MSAL/ADAL used for the above purpose? If so how can I get the access_token using MSAL?
Could someone please point me to some example code?
You can use ITokenAcquisition service. Add a constructor to your Controller, making the ITokenAcquisition service available (used by the ASP.NET dependency injection mechanism)
For example,
public HomeController(ITokenAcquisition tokenAcquisition)
{
this.tokenAcquisition = tokenAcquisition;
}
private ITokenAcquisition tokenAcquisition;
Then you can get the access token like this
var accessToken =
await tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext,
new[] {Constants.ScopeUserRead});
You can refer to this complete sample for more details.
You are correct , ADAL/MSAL are used to acquire tokens in order to call secured Web APIs. If using OpenID connect middleware , in OnAuthorizationCodeReceived event , you can use ADAL/MSAL to acquire access token with authorization code .
If using Azure AD v1.0 , you could refer to this code sample which use ADAL to acquire access token . Check the OnAuthorizationCodeReceived event. Here is another article with code sample .
If using Azure AD v2.0 (to sign-in users with Work and School accounts and Microsoft Personal accounts) , you can refer to below link for code samples which use MSAL :
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2
Your scenario matches the forth code sample : 4-WebApp-your-API .
That codes sample use Microsoft.Identity.Web library which make it easier to build your Web Apps on top of Microsoft identity platform for developers , add your api as scope :
services.AddAzureAdV2Authentication(Configuration)
.AddMsal(new string[] { Configuration["TodoList:TodoListScope"] })
And get token like :
var accessToken = await this._tokenAcquisition.GetAccessTokenOnBehalfOfUser(this._contextAccessor.HttpContext, new[] { this._TodoListScope });

Failed to get AccessToken via authorization code using MSAL 1.1.0-preview in asp.net core

I followed official steps as below to try the scenario "web app calling a Web API in Azure Ad B2C", the only difference is I am using Asp.Net core. I am using AuthorizationCode to get the access token, but it always returns with id token and NULL access token.
Create an Azure AD B2C tenant.
Register a web api.
Register a web app.
Set up policies.
Grant the web app permissions to use the web api.
My code:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
AutomaticChallenge = true,
ClientId = aadB2cSettings.ClientId,
MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
var authCode = context.TokenEndpointRequest.Code;
var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
var cca = new ConfidentialClientApplication(
aadB2cSettings.ClientId,
b2cAuthority,
aadB2cSettings.RedirectUrl,
new ClientCredential(aadB2cSettings.ClientSecret),
new TokenCache(),
null);
try
{
var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
}
catch (Exception ex)
{
throw ex;
}
}
},
Used fiddler to capture the request, it is:
POST
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin
HTTP/1.1
Request Body:
client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F
Return:
{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}
So only id token, no access token. But we should get access token here, right?
Finally found out my failure reason: the request to get AuthorizationCode doesn't contain the target scope. Reflect in code, for OpenIdConnectOption in aspnetcore, the Scope parameter is readonly and its default value is "opened profile".
Scope is readonly in OpenIdConnectOption
So the default authorization code request sent is:
GET
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=......
HTTP/1.1
Thus, using this authorization code in response to get token, even we set right scope in the token request, we still can't get the access code but only id token, because the provide authorization code is only for "openid profile".
To fix this, we need to add target web api scope into the authorization code as well. Here is the how-to-fix code:
Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Scope += $" offline_access {myapiscope}";
return Task.FromResult(0);
},
......
}
In AspNet, we don't need to do this because its scope is not readonly as aspnetcore and can be set directly:
new OpenIdConnectAuthenticationOptions
{
......
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/issues/4 Microsoft have reproduced the issue and working on fix

Resources