Adopt authorities from JWT in Spring Resource Server - spring-security

My Spring OAuth2 client only grants the ROLE_USER authority to authenticated users, ignoring the authorities from resource_access in the provided JWT.
{
"wdb": {
"roles": [
"TestRole",
"TestRoleFoo",
"TestRoleBar"
]
}
How can I setup my OAuth2 client to also grant the authorities from resource_access (TestRole, TestRoleFoo, TestRoleBar)? Am I missing some crucial configuration here?
My configuration in detail
On my Resource Server, I'm using Springs default OAuth2 client with the following configuration:
security:
oauth2:
client:
client-id: wdb
client-secret: some-secret
access-token-uri: http://localhost:8080/auth/realms/master/protocol/openid-connect/token
user-authorization-uri: http://localhost:8080/auth/realms/master/protocol/openid-connect/auth
scope: openid profile email
authorized-grant-types: code
resource:
user-info-uri: http://localhost:8080/auth/realms/master/protocol/openid-connect/userinfo
My Keycloak Authorization Server provides me with the following JWT payload:
{
"jti": "6a666808-2b69-4de0-ab94-9ceebdac13de",
"exp": 1569674641,
"nbf": 0,
"iat": 1569674341,
"iss": "http://localhost:8080/auth/realms/master",
"aud": "account",
"sub": "f19b0443-4cce-495a-8479-ff36f82628fc",
"typ": "Bearer",
"azp": "wdb",
"auth_time": 1569674341,
"session_state": "0a411eda-0efb-4f29-99c4-b54da6298d6c",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"wdb": {
"roles": [
"TestRole",
"TestRoleFoo",
"TestRoleBar"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
"email_verified": true,
"user_name": "sullrich",
"name": "Sebastian Ullrich",
"preferred_username": "sullrich",
"given_name": "Sebastian",
"locale": "de",
"family_name": "Ullrich",
"email": "sebastian#wdb.local"
}
Within my Resource Server, this JWT will be derived to the following OAuth2Authentication:
{
"authorities":[
{
"authority":"ROLE_USER"
}
],
"details":{
"remoteAddress":"0:0:0:0:0:0:0:1",
"sessionId":"... session id ...",
"tokenValue":"... encoded payload ...",
"tokenType":"bearer"
},
"authenticated":true,
"userAuthentication":{
"authorities":[
{
"authority":"ROLE_USER"
}
],
"details":{
"sub":"f19b0443-4cce-495a-8479-ff36f82628fc",
"email_verified":true,
"user_name":"sullrich",
"name":"Sebastian Ullrich",
"preferred_username":"sullrich",
"given_name":"Sebastian",
"locale":"de",
"family_name":"Ullrich",
"email":"sebastian#wdb.local"
},
"authenticated":true,
"principal":"Sebastian Ullrich",
"credentials":"N/A",
"name":"Sebastian Ullrich"
},
"principal":"Sebastian Ullrich",
"credentials":"",
"clientOnly":false,
"oauth2Request":{
"clientId":"wdb",
"scope":[
],
"requestParameters":{
},
"resourceIds":[
],
"authorities":[
],
"approved":true,
"refresh":false,
"responseTypes":[
],
"extensions":{
}
},
"name":"Sebastian Ullrich"
}

Sounds like you need a custom JwtAuthenticationConverter
Spring will only map scopes into granted authorities by default.
You can create a class that extends the default implementation and overrides the extractAuthorities method.
Then you have access to the claims and you can map them to the roles you want.
public class JwtGrantedAuthoritiesConverter extends JwtAuthenticationConverter {
#Override
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
Collection<GrantedAuthority> authorities = super.extractAuthorities(jwt);
if(jwt.containsClaim("roles") && jwt.getClaimAsStringList("roles").contains("TestRole")) {
authorities.add(new SimpleGrantedAuthority("ROLE_TestRole"));
} else {
.........
}
return authorities;
}
Then you plug in your version into the resource server in your WebSecurityConfigurationAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
......
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new JwtGrantedAuthoritiesConverter());
Your roles are a bit more nested i.e. under resource_access . wdb
You can always create a keycloak mapper to add them under roles in the parent node to simplify things.
Here is an example of a resource server that does something similar
https://github.com/wlesniak/effective-oauth2-with-spring-security-and-spring-boot/tree/master/module_8/mod8_support-service

Related

Cant create proper OpenID Connect configuration endpoint (v2)

I want to have an application in azure (simple asp.net mvc application) that keeps users in azure, I want to have that done by azure b2c.
I registered application in azure and put all configurations into appsettings.json what I notice is that the url generated by application does not match the one from azure:
Here's from application:
'https://isthereanynewscodeblast.b2clogin.com/isthereanynewscodeblast.onmicrosoft.com/B2C_1_eclaims_login/v2.0/.well-known/openid-configuration'
Here's from B2C:
'https://isthereanynewscodeblast.b2clogin.com/isthereanynewscodeblast.onmicrosoft.com//v2.0/.well-known/openid-configuration'
Similar but not the same. What I have found is that the url is being generated by AzureADB2COpenIdConnectOptionsConfiguration in this method:
internal static string BuildAuthority(AzureADB2COptions AzureADB2COptions)
{
var baseUri = new Uri(AzureADB2COptions.Instance);
var pathBase = baseUri.PathAndQuery.TrimEnd('/');
var domain = AzureADB2COptions.Domain;
var policy = AzureADB2COptions.DefaultPolicy;
return new Uri(baseUri, new PathString($"{pathBase}/{domain}/{policy}/v2.0")).ToString();
}
And here's my .json
"AzureAdB2C": {
"Instance": "https://isthereanynewscodeblast.b2clogin.com",
"Domain": "isthereanynewscodeblast.onmicrosoft.com",
"ClientId": "guid-of-client",
"CallbackPath": "/signin-oidc",
"SignUpSignInPolicyId": "B2C_1_eclaims_login ",
"ResetPasswordPolicyId": "B2C_1_eclaims_reset",
"EditProfilePolicyId": "B2C_1_eclaims_edit"
},
Which does not match the one from AAD :(
Code is from a nuget: Microsoft.AspNetCore.Authorization
It's not protected nor virtual, so I don't see any option to override it.
So my questions are:
is there a way to handle this somehow, so that application can communicate with azure
is there other way to register app, easy like this:
services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
.AddAzureADB2C(options =>
{
Configuration.Bind("AzureAdB2C", options);
});
//EDIT:
Here's manifest from application registration:
{
"id": "438a430b-4e80-4c6c-8f45-dfca460b2e03",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "44234136-6eee-431f-98ea-668343d7a3fd",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2020-08-18T22:32:28Z",
"groupMembershipClaims": null,
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "user-log-test",
"oauth2AllowIdTokenImplicitFlow": false,
"oauth2AllowImplicitFlow": false,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"preAuthorizedApplications": [],
"publisherDomain": "isthereanynewscodeblast.onmicrosoft.com",
"replyUrlsWithType": [
{
"url": "https://localhost:44395/signin-oidc",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [],
"tokenEncryptionKeyId": null
}
Can you please try using this b2c sample app which will give you an idea how to use b2c points. It comes with pre-configured endpoints (below), which you can replace with your tenant and policy later for testing.
{
"AzureAdB2C": {
"Instance": "https://fabrikamb2c.b2clogin.com",
"ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
"Domain": "fabrikamb2c.onmicrosoft.com",
"SignedOutCallbackPath": "/signout/B2C_1_susi",
"SignUpSignInPolicyId": "b2c_1_susi",
"ResetPasswordPolicyId": "b2c_1_reset",
"EditProfilePolicyId": "b2c_1_edit_profile"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
I saw your app manifest file and found that you have not enabled implicit flow. Please Select the app and go to Authentication and select ID Tokens and Access Tokens.
I tried on the sample shared by #Razi and it is working fine end-to-end.

not able to get photo from graph api from SPA front end web app

I am using ADAL js to authenticate user and i am able to authenticate user successfully. I am able to fetch a token for graph api and read user profile with following url.
GET
https://graph.microsoft.com/v1.0/me
But i am not able to read user profile picture:
https://graph.microsoft.com/v1.0/me/photo/$value
I get the following error
Object { code: "NoPermissionsInAccessToken", message: "The token contains no permissions, or permissions can not be understood.", innerError: {…} }
I have set the required permissions:
Is there any way i can check why i am able to fetch profile but not photo.
Content of JWT sent in header before it received 401 error:
{
"typ": "JWT",
"nonce": "IenxIPCU1ue14Z9bIIxEidRBBCTnL52w4Q",
"alg": "RS256",
"x5t": "huN95IvPf34GzBDZ1GXGirnM",
"kid": "huN95hq34GzBGXGirnM"
}
Body of JWT token:
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/6f1dc6d4-8e90-4593/",
"iat": 1596560469,
"nbf": 1596560469,
"exp": 1596564369,
"acct": 1,
"acr": "1",
"aio": "ATQAy/8QAAAAf64iQ9pAkP+bk/JnXpSNXFPVFqvW/urra8A2QueWm2xaJZM+",
"altsecid": "5::100320A47F8DD5",
"amr": [
"wia"
],
"app_displayname": "graphdemo-dev",
"appid": "dsfkj32-4350-44a4-dd33-f45b7172b0cd",
"appidacr": "0",
"email": "email#domain.com",
"family_name": "faily",
"given_name": "given",
"idp": "https://sts.windows.net/deff24bb-2089-4400378b2/",
"in_corp": "true",
"ipaddr": "70.50.13.18",
"oid": "dskfs77s-5bc6-4fbe-b59a-11fbc2",
"platf": "3",
"puid": "A9BDE43D",
"scp": "profile User.Read User.Read.All User.ReadBasic.All User.ReadWrite User.ReadWrite.All",
"sub": "r4-9Ra9nHTjU-g1PvuXwh18",
"tenant_region_scope": "NA",
"tid": "d4-8e90-4599-af70-13a4289b3",
"unique_name": "email#domain.com",
"uti": "MDGPXbP3lUJMyAA",
"ver": "1.0",
"xms_tcdt": 8700342
}
Note: I removed and updated confidential data with random chars.
When i tried on Graph Explorer:
Need admin approval
Graph explorer (official site)
microsoft.com
Graph explorer (official site) needs permission to access resources in your organization that only an admin can grant. Please ask an admin to grant permission to this app before you can use it.
import AuthenticationContext from 'adal-angular/lib/adal.js';
// KPMG
const config = {
tenant: process.env.VUE_APP_AZUREAD_TENANTID,
clientId: process.env.VUE_APP_AZUREAD_CLIENTID,
cacheLocation: process.env.VUE_APP_CACHE_LOCATION,
redirectUri: process.env.VUE_APP_REDIRECT_URI
};
export default {
authenticationContext: null,
/**
* #return {Promise}
*/
initialize() {
this.authenticationContext = new AuthenticationContext(config);
return new Promise((resolve, reject) => {
if (this.authenticationContext.isCallback(window.location.hash) || window.self !== window.top) {
// redirect to the location specified in the url params.
this.authenticationContext.handleWindowCallback();
}
else {
// try pull the user out of local storage
const user = this.authenticationContext.getCachedUser();
if (user) {
this.authenticationContext.config.extraQueryParameter = 'login_hint=' + user.userName;
resolve();
}
else {
// no user at all - go sign in..
this.signIn();
}
}
});
},
I use below code to get graph api token
acquireGraphApiToken() {
return new Promise((resolve, reject) => {
this.authenticationContext.acquireToken('https://graph.microsoft.com', (error, graphApiToken) => {
if (error || !graphApiToken) {
this.signOut();
return reject(error);
} else {
return resolve(graphApiToken);
}
});
});
},
For Microsoft Graph explorer, you need to sign in with an admin account and do the admin consent like this:
Do the admin consent:
And from the screenshot above, you can see the access token. After you finish the admin consent, you can decode the access token to see if it includes the required permissions.
For you own Azure AD application, I see that you have done the admin consent based on your screenshot. It's hard to say where the problem is. So my suggestion is to try the admin consent endpoint:
// Line breaks are for legibility only.
GET https://login.microsoftonline.com/{tenant}/v2.0/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
&scope=
https://graph.microsoft.com/calendars.read
https://graph.microsoft.com/mail.send
Access this url in a browser using an admin account and finish the consent. If the issue still exists, you can create a new Azure AD application and only add the required permission User.Read (Don't add other permissions).

How to fix "Unsupported AAD Identity" when create a call from webapp?

When I call this from my web app, I'm getting an error code 9000 - "Unsupported AAD Identity".
POST https://graph.microsoft.com/beta/app/calls
request header:
{
"Content-type" => "application/json",
"Authorization" => "Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFBUDB3TGxxZExWVG9PcEE0a3d6U254TkY3UDFxM05tT0xEOHZJVXk0NmFtVWRaV1ZhbGdFUWx2Vkw4Mmp4cS1tZFpwOWdiY1kwdVB4U3ctOGlGd3JRM00zUWlBS29KS08zRzN3czNsNlFmZXlBQSIsImFsZyI6IlJTMjU2IiwieDV0IjoidTRPZk5GUEh3RUJvc0hqdHJhdU9iVjg0TG5ZIiwia2lkIjoidTRPZk5GUEh3RUJvc0hqdHJhdU9iVjg0TG5ZIn0.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC81NGRkYWJiMS1kNjU5LTRhZmYtODNkZi1kYzUwODk2OTI3YjgvIiwiaWF0IjoxNTYzODQxMTQ2LCJuYmYiOjE1NjM4NDExNDYsImV4cCI6MTU2Mzg0NTA0NiwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IkFTUUEyLzhNQUFBQWlhS2hob3hBalByVHJsOEZVc0w0Q2Y2Zkc4M2x4YVpIWXVYOTJaT0w5eDQ9IiwiYW1yIjpbInB3ZCIsInJzYSJdLCJhcHBfZGlzcGxheW5hbWUiOiJNeSBSdWJ5IEFwcCIsImFwcGlkIjoiYTFlMjliY2YtODYxOS00ZjVjLWEzMjAtNmY2N2QzMGZiOTlkIiwiYXBwaWRhY3IiOiIxIiwiZGV2aWNlaWQiOiJjMThiZDJmYS05YzhkLTRlOGItYTUwMi1lYWFlMmI2YzM1NjYiLCJmYW1pbHlfbmFtZSI6IuWFqOS9kyIsImdpdmVuX25hbWUiOiLnrqHnkIbogIUiLCJpcGFkZHIiOiIxNTMuMTU2Ljg5LjYzIiwibmFtZSI6IuWFqOS9kyDnrqHnkIbogIUiLCJvaWQiOiIwZGFmMzVhNS1jZjUyLTQ0ODMtYmM0NS0xM2ExYTBlYWE5Y2MiLCJwbGF0ZiI6IjMiLCJwdWlkIjoiMTAwMzIwMDA0OTE0QkVDMCIsInNjcCI6IkNhbGVuZGFycy5SZWFkIERpcmVjdG9yeS5BY2Nlc3NBc1VzZXIuQWxsIERpcmVjdG9yeS5SZWFkLkFsbCBlbWFpbCBHcm91cC5SZWFkLkFsbCBHcm91cC5SZWFkV3JpdGUuQWxsIG9wZW5pZCBwcm9maWxlIFVzZXIuUmVhZCBVc2VyLlJlYWQuQWxsIFVzZXIuUmVhZEJhc2ljLkFsbCIsInNpZ25pbl9zdGF0ZSI6WyJrbXNpIl0sInN1YiI6IktIZ3dXcEdHMWRRdHlUSGplU3pfQ1RPWk03b0w1bHRocWFUclRSbi1ucm8iLCJ0aWQiOiI1NGRkYWJiMS1kNjU5LTRhZmYtODNkZi1kYzUwODk2OTI3YjgiLCJ1bmlxdWVfbmFtZSI6ImFkbWluQGV4ZW9kZXYuaXRlZS5jby5qcCIsInVwbiI6ImFkbWluQGV4ZW9kZXYuaXRlZS5jby5qcCIsInV0aSI6IlVPRURPdnAzWEVTUU9rckU0dEFFQUEiLCJ2ZXIiOiIxLjAiLCJ3aWRzIjpbIjYyZTkwMzk0LTY5ZjUtNDIzNy05MTkwLTAxMjE3NzE0NWUxMCIsImY3MDkzOGEwLWZjMTAtNDE3Ny05ZTkwLTIxNzhmODc2NTczNyJdLCJ4bXNfc3QiOnsic3ViIjoiS2Mwc1JER3hHZFV5eDR6Qk1ucm9XWDhkNVpBTjVKN1EwSTh5SWZnUUhZZyJ9LCJ4bXNfdGNkdCI6MTU1ODMxNTU3OH0.cQmQLHWUfs8iOOPHf5SmWJYgsjQqLjZq9W5pKZzwRBtiQoOsUHILZkGYKz7_jx0bW-p87Cq6mFzswnoK30smEH6l7VW-gkzrxc2JFuDh-nKvfemTKnI3O_ZjtrEJNp-sWZF5Enm28Mg5Lh4hfBrSiROO8b0gAMytLWx9Qjy3H5x_zzdy34D1B1O8nCFrx217olDzarDCd6KqPtfCqoS00mWqCIwlEvHSi7OCtBV0HaUEl07-hi9hovu-uaHTLRW50fFP9hfoWYOz5qRyidpGNRtR26rUtexlOXtceZKYv5fD_VFNiBdT7d06EiK58UBib08eHjcvNJ6NEhcW0xTOxA"
}
request body:
{
"callbackUri": "https://bot.contoso.com/api/calls",
"mediaConfig": {
"#odata.type": "#microsoft.graph.serviceHostedMediaConfig",
"preFetchMedia": [
{
"uri": "https://cdn.contoso.com/beep.wav",
"resourceId": "1D6DE2D4-CD51-4309-8DAA-70768651088E"
},
{
"uri": "https://cdn.contoso.com/cool.wav",
"resourceId": "1D6DE2D4-CD51-4309-8DAA-70768651088F"
}
]
},
"source": {
"identity": {
"application": {
"id": "RealAppId"
}
},
"languageId": "languageId-value",
"region": "region-value"
},
"subject": "Test Call",
"targets": [
{
"identity": {
"user": {
"id": "RealTargetUserId",
"tenantId": "RealTenantId",
"displayName": "RealName"
}
}
}
],
"tenantId": "tenantId-value"
}
response:
{
"error": {
"code": "UnknownError",
"message": "{
"errorCode": "9000",
"message": "Unsupported AAD Identity.",
"instanceAnnotations": []
}",
"innerError": {
"request-id": "RealRequestId",
"date": "2019-07-23T00:25:12"
}
}
}
I n the request body, RealTargetUserId is an Azure ActiveDirectory UserID with an E1 license provisioned. RealAppId is the registered application id and with Permissions as follows:
Calls.AccessMedia.All
Calls.Initiate.All
Calls.InitiateGroupCall.All
Calls.JoinGroupCall.All
Calls.JoinGroupCallAsGuest.All
According to the documentation, you need one of the following Application scopes to create a call: Calls.JoinGroupCallsasGuest.All, Calls.JoinGroupCalls.All, Calls.Initiate.All, Calls.InitiateGroupCalls.All.
The authentication token you're using, however, is using Delegated scopes. This tells me you're either using OAuth's Authentication Code or Implicit grant flows. Neither of these will work for this operation.
In order to use this endpoint, you'll need to request an App-Only token using the OAuth Client Credentials grant. There are instructions on how to do this in the documentation: Get access without a user.
Also, you're over-requesting permission scopes. For example, there is no need to request User.Read, User.Read.All, and User.ReadBasic.All, you only need User.Read.All to accomplish the same thing. The same goes for Group.Read.All and Group.ReadWrite.All.
To fix issues with "Application is not registered in our store" you need to make sure that you use a Bot Channel Registration instead of just a normal App Registration.
The Bot Channel Registration should also have Microsoft Teams as a registered channel and have calling enabled.
For more information see: https://microsoftgraph.github.io/microsoft-graph-comms-samples/docs/articles/calls/register-calling-bot.html
The final step about adding the bot in teams can be skipped if you only care about API access.

AWS cognito login set AuthFlow to USER_PASSWORD_AUTH in iOS

I’m using AWS Cognito to perform login authentication. When login is successful we get below request body :
Request body:
> {"UserContextData":{"EncodedData":"eyJ..9”},”ClientMetadata":{"cognito:deviceName":"MacBookPro12-01","cognito:bundleShortV":"1.0.0",
> "cognito:idForVendor":"A6FD46FBB205","cognito:bundleVersion":"207",
> "cognito:bundleId":"com.abc.Project-Dev","cognito:model":"iPhone", "cognito:systemName":"iOS","cognito:iOSVersion":"11.3"},
> "AuthParameters":{"SRP_A":"a6..627","SECRET_HASH":"vr..Oo=", "USERNAME":"jay.dubey#abc.com”},**”AuthFlow":"USER_SRP_AUTH"**,
> "ClientId”:”123”}
Now, there is a scenario wherein I’ve to set “AuthFlow” value to “USER_PASSWORD_AUTH”. How can this be done?
The headache with this is that all these values are set in Pods. Below code prints the request body that is added above :
passwordAuthenticationCompletion?.set(result: AWSCognitoIdentityPasswordAuthenticationDetails(username: username, password: password))
If you will look into AWSCognitoIdentityUser in method getSessionWithUserName andPassword you will see that there is a ternary operator switching migration auth that is driven by migrationEnabled Boolean value. In order to switch auth type just configure identity pool like so:
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration (
clientId: clientId,
clientSecret: nil,
poolId: userPoolId,
shouldProvideCognitoValidationData: false,
pinpointAppId: nil,
migrationEnabled: true
)
I found you need to enable the migration in your amplifyconfigurqaton.json so that it uses the USER_PASSWORD_AUTH mode (it will ignore it otherwise):
{
"auth": {
"plugins": {
"awsCognitoAuthPlugin": {
"IdentityManager": {
"Default": {}
},
"CredentialsProvider": {
"CognitoIdentity": {
"Default": {
"PoolId": "xxxx",
"Region": "xxxxx"
}
}
},
"CognitoUserPool": {
"Default": {
"PoolId": "xxxxx",
"AppClientId": "xxxxx",
"Region": "xxxxx",
"MigrationEnabled" : true
}

Spring Boot Resource Server not able to authorize roles with oAuth 2 Access Token

I have the following 3 micro-services in place
Config Server
Auth Server using MongoDB referencing link. I successfully migrated the project from 1.2.4 to 1.3.3
User Service. A Rest Controller and a Resource Server with 3 Get methods.(each for ADMIN,MERCHANT and CONSUMER)
I am looking to restrict access to the GET methods of the REST Controller based on the role of the user.
The Resource Configuration is as follows
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/**").hasAuthority("ROLE_ADMIN")
.anyRequest()
.authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("admin-api");
}
}
To test I am simply trying to lock the User Service for all users except with role ADMIN. However, I get 401 Access Denied. I have also tried hasRole("ADMIN") with same result. If i remove that authorization criteria then the user is rightly authenticated(does not accept wrong access token).
The response from userInfoUri of the auth server is as follows
{
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null,
"tokenValue": "a4244b33-80b2-48db-909d-f8aaaaf45985",
"tokenType": "Bearer",
"decodedDetails": null
},
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"authenticated": true,
"userAuthentication": {
"details": null,
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"authenticated": true,
"principal": "admin#ikarma.com",
"credentials": null,
"client": false,
"name": "admin#ikarma.com"
},
"credentials": "",
"clientOnly": false,
"oauth2Request": {
"clientId": "admin-web",
"scope": [
"trust",
"read",
"write"
],
"requestParameters": {
"grant_type": "password",
"username": "admin#ikarma.com"
},
"resourceIds": null,
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"refreshTokenRequest": null,
"grantType": "password"
},
"principal": "admin#ikarma.com",
"name": "admin#ikarma.com"
}
I am not able to figure out why role based authorization is not working. Any help is kindly appreciated.
Change spring-security-oauth2 dependency to this
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
Make sure that the implementation of JWT encode/decode is the same on the both side resource server and auth server. Also please try on the oauth server to create inMemory the 3 users with the 3 roles:
#Configuration
public class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("user").authorities("read").roles("USER")
.and()
.withUser("admin").password("admin").authorities("read","write").roles("ADMIN");
}
...
}
Try to receive an access_token for these users and then make a request to the resource server including in request the header Authorization: bearer [access_token]
If you will get the same error means that your JWT implementation is not right...
Please take a look about JWT here
https://bshaffer.github.io/oauth2-server-php-docs/overview/jwt-access-tokens/

Resources