Admittedly, this is a first stab at building an Asp.Net Core web api project.. One requirement is to support OAuth2. The Api and Identity server are two separate projects, both started from an Asp.Net core Empty template.
Identity server is up and running, and tokens are being provided via the resource owner flow. Getting the token is fine, scopes and relevant access_token details appear to be correct.
When I issue a get request to my resource end-point, I get the following at first...
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:12886/v1/mystuff
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[2]
Successfully validated the token.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[3]
HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Bearer.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[8]
AuthenticationScheme: Bearer was successfully authenticated.
info: IdentityModel.AspNetCore.ScopeValidation.ScopeValidationMiddleware[0]
Scopes found on current principal: scope: stuffdetails, scope: stuffmover
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[8]
AuthenticationScheme: Bearer was successfully authenticated.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
Authorization was successful for user: 939d72dd-654c-447f-a65d-d0426b1eca59.
So, I can tell middleware is validating my token, reading scopes, and the authenticating the token.
However, immediately following the initial success, I get authorization failures.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed for user: 939d72dd-654c-447f-a65d-d0426b1eca59.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
Executing ChallengeResult with authentication schemes ().
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[13]
AuthenticationScheme: Bearer was forbidden.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action TestApi.StuffController.GetStuff (TestApi) in 32.4439ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 1207.1769ms 403
Here is what I believe are the relevant bits in startup.
ConfigureServices...
services.AddMvcCore()
.AddAuthorization(opts =>
{
opts.AddPolicy("stuffdetails",
policy => policy.RequireClaim("stuffdetails"));
}
)
.AddJsonFormatters();
services.AddOptions();
Configure
-- Note that I know my configOptions are correct because the initial token challenge is successful.
var authServerOptions = new IdentityServerAuthenticationOptions
{
Authority = configOptions.Value.AuthServerSettings.AuthServerURI,
RequireHttpsMetadata = configOptions.Value.AuthServerSettings.RequiresHttpsMetaData,
ApiName = configOptions.Value.AuthServerSettings.ApiName,
AllowedScopes = configOptions.Value.AuthServerSettings.AllowedScopes,
SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt,
AuthenticationScheme = "Bearer",
SaveToken = true,
ValidateScope = true
};
app.UseIdentityServerAuthentication(authServerOptions);
app.UseMvc();
Stuff Controller
[Route("v1/[controller]")]
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
public class StuffController : Controller
{
[HttpGet]
[Authorize(Policy = "stuffdetails")]
public JsonResult GetStuff()
{
return new JsonResult(new
{
Message = "You've got stuff.."
});
}
}
If I remove the Authorize attribute from the GetStuff method, everything is fine because as the log showed, the bearer token is authorized.
The questions:
Is authorization failing because my policy is incorrect, and if so how should it be setup?
If I want to validate a token contains the proper claims, and was authorized, is it correct to use policies as I have?
Am I making a mistake trying to use UseIdentityServerAuthentication instead of UseJwtBearerAuthentication?
Any help is greatly appreciated..
Is authorization failing because my policy is incorrect, and if so how
should it be setup?
What you've got looks correct, but you can easily verify by just removing the 'policy' part of your Authorize attribute: if it now works then the problem is to do with your policy, if it still fails then it is a broader problem than just your policy. I'm assuming you're adding the 'stuffdetails' claim into your access_token with your own implementation of the IProfileService?
If I want to validate a token contains the proper claims, and was
authorized, is it correct to use policies as I have?
Yes that seems to be the aspnet core way of doing custom authorization.
Am I making a mistake trying to use UseIdentityServerAuthentication
instead of UseJwtBearerAuthentication?
I am using the UseIdentityServerAuthentication with the ResourceOwnerPassword flow. I'd be interested to hear if the UseJwtBearerAuthentication approach is preferred or offers other features.
The error on my part was the way I created my policy:
opts.AddPolicy("stuffdetails",
policy => policy.RequireClaim("stuffdetails"));
Should be:
opts.AddPolicy("stuffdetails",
policy => policy.RequireClaim("scope","stuffdetails"));
The policy was supposed to confirm the scopes included "stuffdetails".. A great resource for anyone having trouble is a post by damienbod, Authorization Policies and Data Protection with IdentityServer4 in ASP.Net Cord
Additionally you could implement this leveraging Microsoft.Aspnetcore.Authorization.
The main benefits being that's it's simpler to read and it allows you to specify multiple scopes (union/or).
opts.AddPolicy("stuffdetails", ScopePolicy.Create("stuffdetails", "stuffdetails2"));
instead of
opts.AddPolicy("stuffdetails",
policy => policy.RequireClaim("scope","stuffdetails"));
Related
I have an api which uses AD Token for authorization.
I am trying to fetch the username of the user inside my service component. But im failing to. I have tried this.
val authentication: Authentication = SecurityContextHolder.getContext().authentication
println(authentication.name) // Random short string with 3 "-". Not JWT
println(authentication.details.toString()) // WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null]
println(authentication.authorities.toString()) // Prints Scope [SCOPE_User.Read]
println(authentication.principal) // org.springframework.security.oauth2.jwt.Jwt#xxxxxxxx
The token is from AD and it does contain userdata. The payload contains these fields with user related stuff. I removed the rest.
{,
"family_name": "Wick",
"given_name": "John",
"name": "WickJohn",
"roles": [
"User"
],
"scp": "User.Read",
"unique_name": "wickjo#gmail.com",
"upn": "wickjo#gmail.com",,
}
Anyone have any idea?
I solved it easily just reading the jwt manually.
val authentication = SecurityContextHolder.getContext().authentication
val jwt = authentication.principal as Jwt
println(jwt.claims["name"])
Still would be interesting to find out why i didnt get it automatically
To get username/user info in Spring OAuth2 Resource Server, please try the below:
Make sure to configure resource server in the Authorization Server too.
To get user info by token, resource server provides a filter OAuth2AuthenticationProcessingFilter
The generated token is set into SecurityContextHolder.
Otherwise, When accessing userinfo try including the access token in the header (Authorization Bearer).
If the above doesn't work, then try using On Behalf Of Flow and the code mentioned in this GitHub blog.
For more information, please refer below links:
Azure AD 2.0 Troubleshooting – OAuth Architecture Guidance (authguidance.com)
How to get userinfo by jwt token in spring security oauth2 authorization server? - Stack Overflow
I have developed a blog like project on the django rest framework and oauth2. I am now trying to separate the resource and authentication servers as shown here: https://django-oauth-toolkit.readthedocs.io/en/latest/resource_server.html
I have taken the following steps:
set up the auth server as described in the docs
added the below to settings.py in the auth server
OAUTH2_PROVIDER = {
'SCOPES': {'users': 'user details', 'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups', 'introspection': 'introspection'},
'ACCESS_TOKEN_EXPIRE_SECONDS': 86400, # 1 Day.
}
set up the resource server as described in the docs
added this to settings.py in the resource server:
OAUTH2_PROVIDER = {
'RESOURCE_SERVER_INTROSPECTION_URL': 'http://localhost/o/introspect/',
'RESOURCE_SERVER_AUTH_TOKEN': 'abc',
}
I created the RESOURCE_SERVER_AUTH_TOKEN based on instructions here: Django OAuth- Separate Resource and Authorization Server
To summarise, I created a superuser for the resource server then added an application to the resource server using the admin site, choosing confidential for client type and authorization code for authorization grant type. 'abc' was the random string I chose for the access token.
Nevertheless, I am still facing the following error:
Introspection: Failed to get a valid response from the authentication server. Status code: 403, Reason: Forbidden.
NoneType: None
Do you have any idea of where I may be going wrong from what I've described? Have I understood this correctly and created the RESOURCE_SERVER_AUTH_TOKEN in the correct manner?
I had the same problem when using the 'RESOURCE_SERVER_AUTH_TOKEN'. So instead I used the client_id and client_secret.
Go ahead and try the following:
OAUTH2_PROVIDER = {
'RESOURCE_SERVER_INTROSPECTION_URL': 'http://127.0.0.1:8000/o/introspect/',
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS':
(
client_id,
client_secret
),
}
That is how it worked for me.
In fiddler composer, I execute a call to a local api that is secured by a local identityserver3.
I added the token, and I configured the middleware to validate the token on the server. Tt works fine, in Idenityserver3 log file I see a succesful token validation logmessage for the configured scope.
I assume that, with htis config, each time the api is called, the Idsrv3 middleware calls the token validation endpoint under the hood.
My issue is that fiddler does not show this middleware request, just the call to the api itself. Is this due to fiddler settings or is there another reason this request is invisible to fiddler?
Is there a way to display it?
Thanks
It could be that .NET is not routing the localhost traffic to fiddler proxy. For workaround check this.
http://docs.telerik.com/fiddler/observe-traffic/troubleshooting/notraffictolocalhost
Also, check if you have configured validation mode properly. According to documentation https://identityserver.github.io/Documentation/docsv2/endpoints/identityTokenValidation.html validation endpoint is useful if you have no access to crypto libraries locally.
Valid validation modes are
ValidationMode.Both - Use local validation for JWTs and the validation endpoint for reference tokens
ValidationMode.Local - Use local validation oly (only suitable for JWT tokens)
ValidationMode.ValidationEndpoint - Use the validation endpoint only (works for both JWT and reference tokens)
You can set the validation mode in IdentityServerAuthenticationOptions
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44333/core",
//RequiredScopes = new[] { "write" },
// client credentials for the introspection endpoint
ClientId = "write",
ClientSecret = "secret",
ValidationMode = ValidationMode.Local
});
I have an application which accepts JWTtoken and go through the claims and respond to the request. Once I receive the JWTtoken, I want to validate whether it is issued by the Identity server which I trust.
Any idea how an application can perform JWTtoken validation?
an application simply make call:
/connect/identitytokenvalidation?token=&client_id= and get the token validation done?
Do I need to create TokenClient instance to call RequestAssertionAsync? or I can simply make http get request by passing token value in the query string?
I can get the token value with the following way:
Request.GetOwinContext().Request.Headers["Authorization"];
Any sample would be of a great help.
If your endpoint is running in a Katana pipeline then you can use either the Microsoft JWT bearer authentication middleware, or you can use the IdentityServer3.AccessTokenValidation middleware. Either of these will be the proper validation.
If you don't want to use those and do it manually, then you can use the Microsoft JwtSecurityTokenHandler class to perform the validation.
Here's the relevant lines of code from our sample web api
https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/SampleAspNetWebApi/Startup.cs
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44333/core",
RequiredScopes = new[] { "write" },
});
I have IdentityServer with Membership Reboot and IdentityManager running on a remote server, I've used the Admin UI of IdentityManager to setup a user, and add roles & claims to said user.
I'm developing a WebApi/SPA project that will use the remote server for Auth. Using fiddler I can request a token from the IdentityManagner on the remote box and use this token to against the local WebApi where Authorization is required. If the token is valid the WebApi processes like normal, if the token is bogus I get a 401. Works great.
The problem is when I want additional information about the user none of the claims or identity information is coming across. I'm not sure if the problem is at the IdentityServer side, The WebApi side, or if I'm not doing something correctly when getting my token.
I didn't realize we needed put the claims in the Scope definition. Incase anyone else stumbles upon this I changed my scope to the following
var scopes = new List<Scope>
{
new Scope
{
Enabled = true,
Name = "publicApi",
Description = "Access to our public API",
Type = ScopeType.Resource,
IncludeAllClaimsForUser = true, //I'll filter this down later
}
};
scopes.AddRange(StandardScopes.All);
return scopes;
Further details can be found here