I am using the OpenIdConnectMiddleware in an ASP.NET Core 2.0 app, with Auth0 as authentication.
I have followed this guide to implement authentication via Auth0, and can successfully log in.
Now I want to use the retrieved access token to access a separate API; for this to work I need to include an audience parameter when authorizing with Auth0, as described here.
Since the OpenID Connect middleware handles the authorization with Auth0, and there is no Audience setting on the OpenIdConnectOptions, how can I specify the audience parameter that should be passed to the /authorize endpoint?
Found the answer in this blog post from Jerrie Pelser. I need to use the OnRedirectToIdentityProvider event handler to set audience:
options.Events = new OpenIdConnectEvents {
OnRedirectToIdentityProvider = context => {
context.ProtocolMessage.SetParameter("audience", "https://my/api");
return Task.CompletedTask;
},
...
}
Related
I am working on updating our API (core 3.1) auth to use the latest Microsoft Identity nuget for use with MSAL for an Angular UI application. We have Azure Functions that will call into our API's using a Managed Service Identity and have setup several new app registrations for each API to use with MSAL in Angular. The same API's we call from an Azure function will also be called from the Angular UI. The problem I am running into is that I need to accept up to four different audiences in order not to break auth for everything.
Audiences needed:
client id of the API
https://management.azure.com/ for MSI
https://management.core.windows.net/ for MSI
client id of another app registration we use to generate tokens for automation testing
I am attempting to set the audiences in a list as follows:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options => { }, options =>
{
options.Authority = Configuration["Authentication:Jwt:Authority"];
options.Instance = Configuration.GetSection("AzureAd")["Instance"];
options.ClientId = Configuration.GetSection("AzureAd")["ClientId"];
options.Domain = Configuration.GetSection("AzureAd")["Domain"];
options.TenantId = Configuration.GetSection("AzureAd")["TenantId"];
options.TokenValidationParameters.ValidateAudience = true;
var audience = new List<string>();
audience.Add(Configuration["AzureAd:ClientId"]);
audience.AddRange(new string[] {"https://management.azure.com/",
"https://management.core.windows.net/", "other api client id"});
options.TokenValidationParameters.ValidAudiences = audience;
});
When I attempt to call an endpoint via Swagger using a token created by another app (#4), I get this error:
IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.
I also noticed when looking at the context in the events that none of the audience values I setup at runtime are present when the events trigger. ValidAudience and ValidAudiences are both null.
I need to figure out how to persist these settings in the events as my guess is that is why the audience validation is failing.
How do I get OAuth2 Access Token and Refresh token for C# ASP.NET Core Web API client to authenticate YouTube Data API v3
There is no UI for a username to manually enter their username and password, then receive code to get the token in this scenario. No redirect_uri is required.
How can I get the access token and refresh token
I once solved a similar issue with Microsoft Azure AD, solution on stackoverflow
I just can't find any information regarding Google Cloud Platform .NET clients for this scenario
You can not use client login (username and password) with any Google api since 2015. You will need to use Oauth2 in order to authenticate your user.
You will need to configure the library first.
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
}
Then you can make any call you like to the YouTube API. When this endpoint is hit the user will be prompted to consent to authorization.
[GoogleScopedAuthorize(YouTubeService.ScopeConstants.Readonly)]
public async Task<IActionResult> YouTubeCall([FromServices] IGoogleAuthProvider auth)
{
GoogleCredential cred = await auth.GetCredentialAsync();
var service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = cred
});
// your call to the youTube service here.
}
I recommend having a look at the sample for Asp .net core however it is in google drive you will need to alter it.
The client library should be handing all the access tokens and refresh tokens for you, but if you really want to access them there is a bit of information on how here #1725
I am trying to authenticate CORS origin requests and set Claims principle with the user of internal company single sign on utility. I have the current setting so far, the cookie will never get created on the domain set at the authentication setup.
I have an Angular client application and .Net Core 3.0 Webapi, the requirement is for the client to be able to set authentication for future api calls.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "access_token";
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.Domain = "localhost:xxxx";
});
//CORS
services.AddCors(options =>
{
options.AddPolicy(
"AllowOrigin",
builder => builder.WithOrigins("localhost:xxxx")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod());
});
//Sign In
HttpContext.SignInAsync(
scheme: CookieAuthenticationDefaults.AuthenticationScheme,
principal: new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)),
properties: new AuthenticationProperties { ExpiresUtc = DateTime.Now.Add(120) });
I am testing this all on local so both URLS are localhost with different ports
Angular is hosted: http://localhost:xxxx
WebAPi is hosted :http://localhost:xxx2
http request from Angular to webapi is http://localhost:xxx2/api/auth which has the SignInAsync call, the company single sign does a username but the cookie never gets created. If I remove the options.Cookie.Domain = "localhost:xxxx"; the cookie does get created on the webapi domain http://localhost:xxx2. I must be missing something here.
After reading up some other posts on stackoverflow , it tuned out that AllowAllOrigins will only fix this problem but poses a threat.
So I ended up fixing this issue with JWT - setting authorization token for every request sent from client interface. This issue was caused due to fact that the client and WebApi are hosted on different domains.
We have a Gateway (implemented using Ocelot), which performs both Authentication & Authorization of the calls before it reaches the APIs
For Authentication, the gateway uses JwtBearer like below
services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = JwtBeaerEvents();
options.TokenValidationParameters = TokenValidationParameters(tokenConfig);
});
And, this validates the token correctly.
Apart from this, the Gateway is implemented with Custom Authorization, to which it reads the permission related settings using a custom configuration file. And, this Custom Authorization is added as a middleware
We try to add this Authorization middleware after Authentication middleware, like
app.UseAuthentication().UseAuthorizationMiddleware();
This works for a valid token. However, for an invalid token, irrespective of Authentication got failed, the call is being routed to AuthorizationMiddleware as well. And, based on these findings, looks like we need to go with DI, rather than middleware. But, what we want is a custom implementation for Authorization which accepts the permissions/policy/scope via config file (in the gateway) along with JwtBearer scheme, rather than decorating them in the API attribute. Could anyone throw some light on how to achieve the same?
Your help is much appreciated
The issue is due to the behaviour of .net core. When the Identity's IsAuthenticated flag is false, Http StatusCode is not set to 401 by the framework in case of Token validation failure during Authentication and also it proceeds to the next call. If only we used the Policy based Authorization, it would have been automatically taken care by RequireAuthenticatedUser() while building the Authorization Policy. However, since we are using a custom middleware, introduced one another middleware which replicates what DenyAnonymousAuthorizationRequirement does, like below
var user = httpContext.User;
var userIsAnonymous =
user?.Identity == null ||
!user.Identities.Any(i => i.IsAuthenticated);
if (userIsAnonymous)
{
httpContext.Response.StatusCode = 401;
return Task.CompletedTask;
}
return _next(httpContext);
We placed this middleware in between Authentication & Authorization middlewares and the issue has been resolved
I'm using Ws-Fed Authentication OWIN middleware to authenticate an ASP.NET MVC app with Web API endpoints using ADFS. I'm able to sign in using ADFS successfully, and on my MVC controllers, HttpContext.User.Identity.IsAuthenticated is true - I can see the claims information for the signed in user as well.
However for WebAPI endpoints, User.Identity.IsAuthenticated is false. The claims information for the signed is user is also unavailable. Is there any way that I expose the fact that the user is authenticated for both MVC and WebAPI controllers?
Here is how I am configuring my authentication middleware in the OWIN Startup class:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
ExpireTimeSpan = TimeSpan.FromMinutes(sessionDuration),
SlidingExpiration = true //expiration extended after each request
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = metadata,
Notifications = new WsFederationAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/?loginfailed=loginfailed");
return Task.FromResult(0);
}
}
});
Try adding a "Name Id" claim to ADFS:
http://darb.io/blog/2014/06/30/WebAPI-and-ADFS-as-external-login-provider/
So I discovered the answer to this question by examining the OWIN cookie authentication middleware source code on CodePlex. Cookies created using the middleware by an MVC controller are created differently from cookies created Web API. MVC cookies are a reference to user information stored in session, and since Web API is completely stateless (no session), cookies created in MVC can not be used in Web API.
In addition, it is bad practice to use cookie authentication in Web API anyways; bearer token authentication is a preferable option.
In my case where I needed to use Ws-Federation authentication, the solution was to:
Add bearer token authentication middleware to my app
Create a Web API endpoint (ideally cryptically named) that will securely receive Ws-Federation claims, perform validation to ensure the request really came from your MVC controller, use them to generate a bearer token, and respond with the generated bearer token
Upon authenticating in MVC, serialize the claims, and marshal them over to Web API using the endpoint created earlier
Add the bearer token to a hidden field in the SPA
Many, many thanks to #Juan for providing me with feedback and links to point me in the right direction.