I have a JHipster Gateway and Microservice that are currently configured to use OAUTH/OIDC via Keycloak. Currently all resources are available to the authenticated user. I want to use the fine-grained security available in keycloak (enabling authorization on the JHipster Microservice client id) to further secure by resource id (as referenced in Keycloak documentation)
https://www.keycloak.org/docs/latest/authorization_services/index.html#_resource_server_enable_authorization. For example, restrict */api/companies/{id} to only certain Keycloak users/groups.
It this supported in Jhipster? Based on the logs, it looks like AccessControlFilter can be configured to restrict, but It's not clear to me how this connects to Keycloak fine-grained resource authentication.
2018-04-15 07:11:20.905 DEBUG 18184 --- [ XNIO-2 task-8] c.e.gateway.aop.logging.LoggingAspect : Enter: com.example.gateway.web.rest.AccountResource.getAccount() with argument[s] = [org.springframework.security.oauth2.provider.OAuth2Authentication#2ea9380c: Principal: user; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, sessionId=<SESSION>, tokenType=bearertokenValue=<TOKEN>; Granted Authorities: company.jcb.role1, ROLE_USER]
2018-04-15 07:11:20.907 DEBUG 18184 --- [ XNIO-2 task-8] c.e.gateway.aop.logging.LoggingAspect : Exit: com.example.gateway.web.rest.AccountResource.getAccount() with result = com.example.gateway.domain.User#3a45e83a
2018-04-15 07:11:22.208 DEBUG 18184 --- [oundChannel-104] c.e.g.web.websocket.ActivityService : Sending user tracking data ActivityDTO{sessionId='hchu0fkx', userLogin='user', ipAddress='/0:0:0:0:0:0:0:1:54212', page='/', time='2018-04-15T13:11:22.208Z'}
2018-04-15 07:11:24.634 DEBUG 18184 --- [ XNIO-2 task-19] c.e.g.g.a.AccessControlFilter : Access Control: allowing access for /micro/api/companies, as no access control policy has been set up for service: micro
Since the Keycloak Resource Server authorization as described in https://www.keycloak.org/docs/latest/authorization_services/index.html#_resource_server_enable_authorization was not supported by Jhipster, to secure resources more granularly I decided to:
Upon creation of a resource I want to secure in the microservice, I dynamically create Keycloak group hierarchies via the Keycloak API. For example, if I want to secure access to companies 123 and 456 and I have roles: Account Manager,Employee, Supervisor I create the Keycloak group hierarchies
123
- Account Manager
- Employee
- Supervisor
456
- Account Manager
- Employee
- Supervisor
User accounts can be assigned to the various groups. For example, user "John" might have access to company "456" and role "Employee"
Added the group hierarchies as a claim to the JWT token via the Keycloak Mappers (this was a configuration in the Keycloak client configuration/Mappers )
In the microservice, use the added JWT group claim for any queries accessing resources that are restricted.
Related
We want to achieve an authorization at our APIs.
Ex. We have API-A and API-B and both are exposed to our different consumers.
We have setup of scope based authorization in place with IdentityServer4 where we decorate endpoints with different policies. With IdentityServer4 we are able to achieve this as IdentityServer4 token has scopes claims present in all the grant types but with Azure AD, we found we can't have scope claim in token generated with Client Credential flow.
In our case, Web API B is also exposed to consumers and again they have scope based authorization. To call, Web API B from Web API A we use client credential flow and it will not have scopes claim in token so we are not able to authorize our call to Web API B.
How to achieve scope based authorization with Azure AD in microservices architecture where we call other context APIs from one context.
When you are using client credential flow and using
application permission , you get roles and not scope i.e; scp claim in the token.
Application permissions are sort of roles given to the application
itself and the scope in client credentials should be used as
api://<APP_ID>/.default . They only apply when doing client
credentials authentication, where no user is involved.
See quickstart to configure app access web-apis
Scopes are usually delegated permissions that only apply when a
user is involved in the login process. They allow you to act on
behalf of a user i.e; In the user context only, we will get
scp claims in case of client credential flow.
See azure-ad-scope-based-authorization
So , If you want delegated permissions then you will have to use implicit grant flow instead of client credentials.
As scopes in expose an api page are for Authorization Code Grant flows and where the user is involved, in this case (client credential) its not possible, we have to add our own scopes that is availible for applications to use which are indirectly called roles that we need to add in the manifest itself under approles in the app registration or through the app roles blade.
ex:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8dxxxxxxxxxxxxf98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
After that , those has to be granted admin consent.
So now when requesting a token with a default scope of api://<app id>/.default the "scopes" are returned in the roles claim.
So we can use role claim for authorization purpose.
Also as a work around
Try to make sure to add additional scope like profile, offline_access open_id.
And give response_type=token instead of id_token
Example request:
......&redirect_uri=https://jwt.io&scope=openid profile offline_access&response_type=token&prompt=login
References:
Scope-based authorization in your API with Azure AD – the IT generalist (wordpress.com)
Scope is not being added to Access Token returned from Azure Ad - Stack Overflow
EDIT:
To call a web api from other , there need to be scopes defined in
one api i.e (api2 that you want to call) and those scopes need to be
selected in calling api(api1) . Please go through the process
here
When login in first Api include scope in the request and also try
response type as Token and see if scp available or then with idtoken
https://tenant.b2clogin.com/tenant.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_TenantSignUpIn&client_id=<appid>&nonce=defaultNonce&redirect_uri=https://jwt.ms&scope=openid offline profile https://tenant.onmicrosoft.com/b2capi/write https://tenant.onmicrosoft.com/b2capi/read https://tenant.onmicrosoft.com/b2capi/user_impersonation&response_type=id_token&prompt=login.
Please note that scopes are present as roles depending on the flow
type.
I get IntegratorKey xxxx for user Id xxx does not have access to API version v2 error when calling the DocuSign rooms API. My requests work against the Esign API. I've gone through the consent process. Here's my code:
from docusign_esign import ApiClient
import requests
api_client = ApiClient()
oauth = api_client.request_jwt_user_token(
client_id=integration_key,
user_id=user_id,
oauth_host_name='account.docusign.com',
private_key_bytes=private_key,
expires_in=3600,
scopes=(
'signature',
'impersonation',
'dtr.company.read',
'dtr.company.write',
'dtr.rooms.read',
'dtr.rooms.write',
'dtr.documents.read',
'dtr.documents.write'
)
)
headers = {
'Authorization': f'Bearer {oauth.to_dict()["access_token"]}',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
rep = requests.get(
f'https://rooms.docusign.com/restapi/v2/accounts/{account_id}/rooms',
headers=headers
)
rep.text returns {"message":"IntegratorKey xxxx for user Id xxxx does not have access to API version v2. Attempted to access http://rooms.docusign.com/restapi/v2/accounts/xxx/rooms","errorCode":null}
You cannot use the Rooms API in production without first going through the Go-Live process.
The Go-Live process for the Rooms API is a bit different at this time.
Email go-live#docusign.com with the words "Rooms go-live" in the subject line, providing the following information:
The name and email of a Production Admin user, who can be any member
of the account with full administrative permissions
Your production API Account ID
Your demo API Account ID
The integration key of the application in the demo environment to be
pushed into production
The date range within which the 20+ required Rooms API calls were
performed
More information can be found here - https://developers.docusign.com/rooms-api/guides/golive
Items to check:
Did this work in the developer sandbox (demo.docusign.net) with (account-d.docusign.com as the IdP)?
Did you complete go-live process?
In production, you need to create a new private key for the production version of the integration key. The demo (developer sandbox) and production integration keys share the same guid value but are actually two separate keys with two sets of settings.
Is the user Id from a production account?
Did the user Id grant consent to the integration key within the production system? Either via Administrative Consent (blanket consent) or via Individual Consent.
First try with just the signature (the impersonation scope is not needed). The problem could be that your integration key needs special permissions to access the dtr.* scopes
I'm trying to set up SSO between our (regular, not AKS) kubernetes clusters and Azure AD.
Since I don't know how to forward the token to the kube-dashboard, I'm just currently trying with kubectl binary installed on my computer.
It works when no groups are involved, but we want to filter by security group (accounts on AAD are synced from our onprem Active Directory), no kube RBAC involved.
Setup is inspired by https://medium.com/#olemarkus/using-azure-ad-to-authenticate-to-kubernetes-eb143d3cce10 and https://learn.microsoft.com/fr-fr/azure/aks/azure-ad-integration :
web app for kube api server configured to expose its API (add scope etc...) with app ID : abc123
native app for client kubectl configured with addition of api permission from the web app, with app ID : xyz456
kube api server yaml manifest , I add :
- --oidc-client-id=spn:abc123
- --oidc-issuer-url=https://sts.windows.net/OurAADTenantID
config kubectl binary :
kubectl config set-cluster test-legacy-2 --server=https://192.168.x.y:4443 --certificate-authority=/somelocation/ca.pem
kubectl config set-credentials USER#mydomain.com --auth-provider=azure --auth-provider-arg=environment=AzurePublicCloud --auth-provider-arg=client-id=xyz456 --auth-provider-arg=tenant-id=OurAADTenantID --auth-provider-arg=apiserver-id=abc123
Also in the Azure client app manifest, had to specify :
"allowPublicClient":true,
"oauth2AllowIdTokenImplicitFlow":true
Otherwise had error "Failed to acquire a token: acquiring a new fresh token: waiting for device code authentication to complete: autorest/adal/devicetoken: Error while retrieving OAuth token: Unknown Error".
Found on https://github.com/MicrosoftDocs/azure-docs/issues/10326
Issues start when trying to filter on some security group that I find in the JWT as per https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens
I am receiving a format error even though the JWT Azure sends me does contain the groups in the right format (json array of strings)
Config :
In azure web app manifest to have the groups in my JWT :
"groupMembershipClaims": "SecurityGroup",
kube api server yaml manifest :
- --oidc-groups-claim=groups
- --oidc-required-claim=groups=bbc2eedf-79cd-4505-9fb4-39856ed3790e
with the string here being the GUID of my target security group.
I am receiving error: You must be logged in to the server (Unauthorized) on output of kubectl and the kube api server logs provide me this authentication.go:62] Unable to authenticate the request due to an error: [invalid bearer token, [invalid bearer token, oidc: parse claim groups: json: cannot unmarshal array into Go value of type string]]
But I don't understand why it is not happy cause when I decode the JWT I do have
"groups": [
"00530f35-0013-4237-8947-6e3f6a7895ca",
"bbc2eedf-79cd-4505-9fb4-39856ed3790e",
"17dff614-fd68-4a38-906c-69561daec8b7"
],
which to my knowledge is a well-formatted json array of strings...
Why does the api server complain about the JWT ?
Ok so, Required claims must be a string, not an array of strings
But I found a workaround.
Don't use oidc-groups-claim and oidc-required-claim
In Azure, go to the Properties of the API server App.
Select Yes in "User assignment required"
In "Users and groups" add the specific Security Group you want to filter on
To test : Remove yourself from the Security Group
Wait for the token to expire (in my case it was 1 hour)
You can't log in anymore
I am using Azure AD as identity provider for Okta, with SAML2 protocol. I am able to login just fine. However, I want to send the user's phonenumber from Azure AD to Okta, but Im having problems.
I tried following this guide to add a custom claim, and I added one with these values:
Name: mobilePhone
Value: user.telephonenumber (the only one related to phonenumber I could find)
Namespace: http://schemas.xmlsoap.org/ws/2005/05/identity/claims
I have also added my phonenumber on my profile (Home > Company > Users - All Users > Me - profile) in Azure Active Directory. However, when I login and check the SAML response using my extension, the claim for mobilePhone is nowhere to be found. Why is it missing? Chances are I added my phonenumber on the wrong places, but shouldnt there be an empty claim somewhere?
We are trying to set up an authentication/authorization-process with the following requirements:
Authentication: Done by Azure AD.
Authorization: Only members of a specific security groups should be allowed to access the app.
While the authentication part seems to work without problems, we are stuck at the authorization part. We are using Express and Passport.
Azure AD some tokens to req.headers, e.g.
x-ms-token-aad-access-token
x-ms-token-aad-refresh-token
x-ms-token-aad-id-token
We are currently using the id-token together with the passport-azure-ad BearerStrategy to check the security groups of the user against the allowed security groups.
The problem is: As soon as the id-token expires, the application won't let us access the app. Assuming setting {session: true} in passport could solve this issue, we enabled the session, but no luck.
Doing some more research I found this post: How to refresh an ID Token from Azure AD in a Web App?, which states that only access-tokens can be refreshed, but ID tokens cannot and should not.
Examining the x-ms-token-aad-access-token and the x-ms-token-aad-refresh-token, we found that they don't have the JWT-structure, e.g
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1THdqcHdBSk9NOW4tQSJ9.eyJhdWQiOiJodHRwczovL3NlcnZpY2UuY29udG9zby5jb20vIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvN2ZlODE0NDctZGE1Ny00Mzg1LWJlY2ItNmRlNTdmMjE0NzdlLyIsImlhdCI6MTM4ODQ0MDg2MywibmJmIjoxMzg4NDQwODYzLCJleHAiOjEzODg0NDQ3NjMsInZlciI6IjEuMCIsInRpZCI6IjdmZTgxNDQ3LWRhNTctNDM4NS1iZWNiLTZkZTU3ZjIxNDc3ZSIsIm9pZCI6IjY4Mzg5YWUyLTYyZmEtNGIxOC05MWZlLTUzZGQxMDlkNzRmNSIsInVwbiI6ImZyYW5rbUBjb250b3NvLmNvbSIsInVuaXF1ZV9uYW1lIjoiZnJhbmttQGNvbnRvc28uY29tIiwic3ViIjoiZGVOcUlqOUlPRTlQV0pXYkhzZnRYdDJFYWJQVmwwQ2o4UUFtZWZSTFY5OCIsImZhbWlseV9uYW1lIjoiTWlsbGVyIiwiZ2l2ZW5fbmFtZSI6IkZyYW5rIiwiYXBwaWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctODkwYS0yNzRhNzJhNzMwOWUiLCJhcHBpZGFjciI6IjAiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJhY3IiOiIxIn0.JZw8jC0gptZxVC-7l5sFkdnJgP3_tRjeQEPgUn28XctVe3QqmheLZw7QVZDPCyGycDWBaqy7FLpSekET_BftDkewRhyHk9FW_KeEz0ch2c3i08NGNDbr6XYGVayNuSesYk5Aw_p3ICRlUV1bqEwk-Jkzs9EEkQg4hbefqJS6yS1HoV_2EsEhpd_wCQpxK89WPs3hLYZETRJtG5kvCCEOvSHXmDE6eTHGTnEgsIk--UlPe275Dvou4gEAwLofhLDQbMSjnlV5VLsjimNBVcSRFShoxmQwBJR_b2011Y5IuD6St5zPnzruBbZYkGNurQK63TJPWmRd3mbJsGM0mf3CUQ
They don't contain any dots and thus don't pass the JWT-verification.
Resulting in the following question:
What is the correct way to check security groups of a user against specified allowed security groups?
You can do it through passport in one call, you do not need to do extra calls to other api layers, as seams to be suggested in multiple posts online. Using the v2 endpoint and defining a scope you can choose what you have access to and what you receive back in the token. Some options, including security group do require you to modify the manifest, see below.
In your Azure Active Directory go to the App registration you are using the authenticate users. In the manifest for that app registration change the groupMembershipClaims from null to "SecurityGroup" or "All" if want to include office groups etc.
{
"id": "some-id",
"accessTokenAcceptedVersion": null,
"allowPublicClient": false,
"appId": "some-id",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2018-11-15T17:49:23Z",
"groupMembershipClaims": "SecurityGroup",
"identifierUris": [ ...............
It then populates the Groups field with an array of the groups using their Object ID.
So you can get the array at req.user._json.groups
if (req.user._json.groups.indexOf('some-group-Object-ID') > -1) {
//In Group
} else {
//Not in Group
}
Apart from checking group claims in ID token , you could also call microsoft graph api to get the groups that the user is a direct member of :
POST https://graph.microsoft.com/beta/me/getMemberGroups
Content-type: application/json
Content-length: 33
{
"securityEnabledOnly": true
}
Or using Auzre AD Graph api :
POST https://graph.windows.net/myorganization/{resource_collection}/{resource_id}/getMemberGroups?api-version
Content-Type: application/json
{
"securityEnabledOnly": false
}
For how an App Service Web, Mobile, or API app can be configured to call the Azure Active Directory Graph API on behalf of the logged-in user. You could refer to below document which show detail steps:
https://cgillum.tech/2016/03/25/app-service-auth-aad-graph-api/
After authentication, access token can be fetched directly from a built-in request header (x-ms-token-aad-access-token) and you could make a http request to call graph api to get the group information .