DRF and Oauth2: creating resource and authorization servers - django-rest-framework

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.

Related

Implementing Incremental Authorization in passport.js with the Google API OAuth 2.0

I'm currently using passport.js with the strategy from passport-google-oauth20 and I want to implement incremental authorization. I'm using yarn as my package manager and my server also uses the Nodejs and Express packages.
When a user first logs in, all I need is their basic gmail information like their email address, so I specify the "email" scope:
router.get('/',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['email'],
accessType: 'offline',
prompt: 'consent'
}));
When they are on a different page, they can request that the site send an email from their account so I now need to add the "https://www.mail.google.com" scope to the session. I couldn't find any documentation for it on the passport.js website and google has a page on it, but I don't think that's setup since I'm using passport.js. I've tried using
router.post('/sendEmail',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['https://www.mail.google.com'],
accessType: 'offline',
include_granted_scopes: true,
prompt: 'consent',
failerRedirect: '/auth',
successRedirect: '/google/authorizedSendEmail'
}));
but I get the error
Failed to load https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&hd=(myDomain)&response_type=code&redirect_uri=(myCallbackURL)&scope=https%3A%2F%2Fwww.mail.google.com%2Fauth%2Fuserinfo.email&client_id=(usersID).apps.googleusercontent.com:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '(my domain)' is therefore not allowed access.
when requesting it.
If I include "https://www.mail.google.com" in the initial scope and don't have the second authentication call, then it does work; an email is sent from the user's account. But, of course that isn't incremental authorization.
Also, I think incremental authorization should have its own tag, but I don't have the rep to do so, could someone add it please?

OpenID Okta initiated login `AuthSdkError: Unable to parse a token from the url`

Im follow the react-hosted-login sample project here: https://github.com/okta/samples-js-react/tree/master/okta-hosted-login
I created my application in okta like so:
I configured the react-sample appropriately
export default {
oidc: {
clientId: '0oaewvbvbyZdmYZb60h7',
issuer: 'https://dev-572586.oktapreview.com/oauth2/default',
redirectUri: 'http://localhost:8080/implicit/callback',
scope: 'openid profile email',
},
resourceServer: {
messagesUrl: 'http://localhost:8000/api/messages',
},
};
When I run the app, I can successfully login when I click the login button in the react app. i.e It takes me to my okta sign-in page and redirects back with the access token and all that stuff
However, when the login is initiated from okta, I get the following error AuthSdkError: Unable to parse a token from the url:
What am I doing wrong?
I can assume It's because your Login Redirect URL's and Initiate Login URL
Are the same values on general setting of your app in okta
Try commenting out this if you have disabled PKCE flow,
//pkce: false
One thing you might check:
Make sure you've got the #okta/okta-react package installed (NPM), you've imported the ImplicitCallback component, and your routing has the route to it:

Spring Keycloak adapter Permissions Policy Enforcer. How to set it up

First of all I'm using
keycloak-authz-client-3.3.0.Final
spring boot 1.5.8.RELEASE
spring-boot-starter-security
I've been playing with Keycloak spring adapter exploring the examples since we want to adopt it to our project.
I was able to make it run for Roles easily using this tutorial:
https://dzone.com/articles/easily-secure-your-spring-boot-applications-with-k
After that I moved to permissions and that's when it gets trickier (that's also our main goal).
I want to achieve something like described in here (9.1.2):
http://www.keycloak.org/docs/2.4/authorization_services_guide/topics/enforcer/authorization-context.html#
To get permissions you need to setup in Keycloak Authorization, credentials, and then create Resources or Scopes and Policies to be able to create permissions (it took me a while but I got it working). Testing in the Evaluater everything seems fine.
Next step was to get user permissions on the Spring side. In order to do that I had to enable:
keycloak.policy-enforcer-config.enforcement-mode=permissive
The moment I enable this I get everytime this exception
java.lang.RuntimeException: Could not find resource.
at org.keycloak.authorization.client.resource.ProtectedResource.findAll(ProtectedResource.java:88)
at org.keycloak.adapters.authorization.PolicyEnforcer.configureAllPathsForResourceServer...
...
Caused by: org.keycloak.authorization.client.util.HttpResponseException:
Unexpected response from server: 403 / Forbidden
No matter what address I hit in the server.
So I started to investigate what was the root of the problem. Looking at some examples how to manually get the permissions I actually got them in postman with the following request:
http://localhost:8080/auth/realms/${myKeycloakRealm}/authz/entitlement/${MyKeycloakClient}
including the header Authorization : bearer ${accessToken}
response was {"rpt": ${jwt token}} that actually contains the permissions
So knowing this was working it must be something wrong with the Spring adapter. Investigating a bit further on the Keycloak exception I found that that error was occurring the moment the adapter was getting all the resources. For that it was using the following url:
http://localhost:28080/auth/realms/license/authz/protection/resource_set
with a different token in the headers (that I copied when debugging)
So when I tried it in postman I also got a 403 error, but with a json body:
{
"error": "invalid_scope",
"error_description": "Requires uma_protection scope."
}
I've enabled and disabled all uma configuration within keycloak and I can't make it work. Can please someone point me into the right direction?
Update
I've now updated Keycloak adapter to 3.4.0.final and I'm getting the following error in the UI:
Mon Nov 20 10:09:21 GMT 2017
There was an unexpected error (type=Internal Server Error, status=500).
Could not find resource. Server message: {"error":"invalid_scope","error_description":"Requires uma_protection scope."}
(Pretty much the same I was getting in the postman request)
I've also printed all the user roles to make sure the uma_protection role is there, and it is.
Another thing I did was to disable spring security role prefix to make sure it wasn't a mismatch on the role.
Update 2
Was able to resolve the 403 issue (you can see it in the response below).
Still getting problems obtaining KeycloakSecurityContext from the HttpServletRequest
Update 3
Was able to get KeycloakSecurityContext like this:
Principal principal = servletRequest.getUserPrincipal();
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) principal;
OidcKeycloakAccount auth = token.getAccount();
KeycloakSecurityContext keycloakSecurityContext = auth.getKeycloakSecurityContext();
AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext();
The problem now is that the AuthorizationContext is always null.
I've managed to get it working by adding uma_protection role to the Service Account Roles tab in Keycloak client configuration
More information about it here:
http://www.keycloak.org/docs/2.0/authorization_services_guide/topics/service/protection/whatis-obtain-pat.html
Second part of the solution:
It's mandatory to have the security constrains in place even if they don't mean much to you. Example:
keycloak.securityConstraints[0].authRoles[0] = ROLE1
keycloak.securityConstraints[0].securityCollections[0].name = protected
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /*
Useful demos:
https://github.com/keycloak/keycloak-quickstarts
This code works for me:
HttpServletRequest request = ...; // obtain javax.servlet.http.HttpServletRequest
Principal userPrincipal = request.getUserPrincipal();
KeycloakPrincipal < KeycloakSecurityContext > keycloakPrincipal =
(KeycloakPrincipal < KeycloakSecurityContext > ) userPrincipal;
KeycloakSecurityContext securityContext =
keycloakPrincipal.getKeycloakSecurityContext();
If you faced "java.lang.RuntimeException: Could not find resource" error and you are using Keycloak docker container, The error can be because of this. I dont have enough information about docker. Hence I download keycloak as a zip file and run it. It works properly.

Authorization with Asp.Net Core WebAPI

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"));

Missing Claims and Identity Info with IdentityServer v3

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

Resources