I’d like to have 2 identity providers, e.g.:
Keycloak will be my main provider (some users will be here)
External provider will be my second provider (I’ll receive the bearer token directly)
In a future I could have more external providers.
So the idea is:
The resource server (Spring Security 5 resource-server) will receive the Bearer Token, sometimes is a Keycloak token, sometimes is an external token.
Is it possible to delegate every token to Keycloak it will discovers which is the provider and validate against them?
Bearer Token from Keycloak: 1234
Bearer Token from external provider: 5678
Client (sends Bearer 1234) -> Resource Server (Spring Boot MS) -> Keycloak -> Valid Token
Client (sends Bearer 5678) -> Resource Server (Spring Boot MS) -> Keycloak -> Valid Token in External Provider
Thank in advance
Related
SETTUP:
We have an application which needs to authenticate itself to an API with help of ADFS. For this it seems suitable to use Client Credentials Grant Flow;
https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-openid-connect-oauth-flows-scenarios#client-credentials-grant-flow
So in ADFS I created a new application group with a server application and a Web API with the following setup,
Server Application:
Client Id: client1
Client Secret: (some-guid)
Web API:
Relying parties: client1
Access control policy: Permit everyone
Client permmissions --> Client Application: client1
Permitted scopes: openid profile allatclaim
Then I request a token via https://.../adfs/oauth2/token with
client_id=client1
client_secret=(some-guid)
grant_type=client_credentials
I received a JWT token with aud and appid as expected:
"aud": "microsoft:identityserver:client1"
"appid": "client1"
PROBLEM:
The problem becomes when I want to add another client to use with the API. So I change my setup to,
Server Application 1:
Client Id: client1
Client Secret: (some-guid)
Server Application 2:
Client Id: client2
Client Secret: (some-guid)
Web API:
Relying parties: client1, client2
Access control policy: Permit everyone
Client permmissions --> Client Application: client1, client2
Permitted scopes: openid profile allatclaim
Then when I request a JWT token with the credentials of client2 (client_id and client_secret) I get:
"aud": "microsoft:identityserver:client1"
and the same if I use client1. I tried to add different relying parties in the Web API and it seems like it always sets the aud in the JWT to the first RP in the list (alphabetic order). For instance, if I also add 123 as a RP in the Web API then the "aud" becomes "microsoft:identityserver:123" for both client1 and client2.
If I instead remove all relying parties and add a the url to my api then the aud in JWT token becomes "urn:microsoft:userinfo".
From what I have read, the aud field in the JWT token should contain all principals who will process the JWT token. See https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3.
QUESTIONS:
How should a setup my Application group to be able to have multiple clients?
Can ADFS sent multiple audiences instead of only the first one?
Or have I used the wrong approach?
Kind regards, Jesper
Through trial and error I finally find how to solve this!
The relying parties in the Web API should only contain identifiers for the Web API (not the Server Applications) for instance this could be an address like "https://mywebapi.com".
Client permission --> Client Applications should contain the Server Applications that should be allowed to talk to this server.
Then when requesting a JWT token from ADFS you also need to include the parameter "resource" which value should be one of the identifiers of the Web API. Hence the call body of the call becomes
client_id=client1
client_secret=(some-guid)
grant_type=client_credentials
resource=https://mywebapi.com
I tried to search for the resource field to find out more about it but couldn't find any explanation or any site using it.
I use [JWT for Client Authentication][1] in [Keycloak][2]:
POST /token.oauth2 HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=vAZEIHjQTHuGgaSvyW9hO0RpusLzkvTOww3trZBxZpo&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3A
client-assertion-type%3Ajwt-bearer&
client_assertion=eyJhbGciOiJSUzI1NiJ9.
eyJpc3Mi[...omitted for brevity...].
cC4hiUPo[...omitted for brevity...]
I get :
assess_token
refresh_token
token_type
expires_in
When I try to refresh token I send refresh_token itself, grant type refresh_token and get:
"error": "unauthorized_client",
"error_description": "INVALID_CREDENTIALS: Invalid client credentials"
}```
when I specify `client_id` I get:
```{
"error": "invalid_client",
"error_description": "Parameter client_assertion_type is missing"
}```
If I specify `client_assertion_type` I get error that `client_assertion` itself is missing, so I literally have to provide parameters I provided when retrieved access token.
How that refreshing process actually should work?
[1]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-bearer-12#section-2.2
[2]: https://www.keycloak.org
This could very well be a limitation or policy defined by Keycloak. RFC7523 (JWT for Client Authentication) does allow to enable client credentials when JWT authentication is present. This is highlighted from 3.1. Authorization Grant Processing
JWT authorization grants may be used with or without client
authentication or identification. Whether or not client
authentication is needed in conjunction with a JWT authorization
grant, as well as the supported types of client authentication, are
policy decisions at the discretion of the authorization server.
However, if client credentials are present in the request, the
authorization server MUST validate them.
So even if Keycloak support JWT client authentication, it may still require client credentials to be present in the refresh token request. But also, it could be a limitation from their end.
Additionally, token refresh is defined through RFC6749 - The OAuth 2.0 Authorization Framework. According to it's section 6, refresh token request must contain client credentials when client is a confidential client (simply a client which was created with id and a password). If what you seen is not a limitation, then guess Keycloak adhere to RFC6749 and require you to send client credentials in token refresh request.
Does client_credentials grant type support a refresh token scenario?
How should access_token expiry be handled when using client_credentials grant type?
I have an authorization service and secured services behind a proxy service (Zuul with EnableOAuth2Sso) which acts as a gateway for all requests coming from client application.
Here is the flow I have:
A proxy service (zuul) that accepts requests (rest api) from client application
Proxy service invokes Authorization Service api by posting client_id, client_secret and grant_type (client_credentials) and gets access_token, refresh_token, and expire time from response
Proxy Service routes the original request to protected services as per zuul route mapping.
This flow works fine but looking at the code in ClientCredentialsAccessTokenProvider I noticed that 'supportsRefresh' returns false and 'refreshToken' methods returns null. Does this mean that when the access_token expires any subsequent requests from client applications to the proxy service (zuul) will fail?
client_credentials OAuth grant servers the need of machine-to-machine authentication, so there is no need to refresh the token.
As result, in Spring Security OAuth's ClientCredentialsAccessTokenProvider, supportsRefresh returns false and refreshToken methods returns null.
In fact, your authorization server and resource server are all in same place (which means the token generation is pretty cheap), quite much like our setup. I suggest you can just set a short lifespan (like 10 minutes) for access token, and treat them self-disposable, and get access token every time when you want to touch the secured resource.
I have installed and configured JWT Grant Type in WSO2 IS 5.3.0 following this guide
Then I have configured a Service Provider enabling the OAuth/OpenID Connect Inbound Authenticator.
I am able from a javascript client to authenticate the user exploiting the Oauth 2 protocol with open-id scope obtaining a valid JWT token (JWTToken).
Finally I tried to make a POST request to https://****/oauth2/token?grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=JWTToken using a REST Client and setting Content-Type=application/x-www-form-urlencoded.
When I do the request the WSOIS Server asks for a username and a password. I am able to pass this step providing a valid clientId and clientSecret.
Unfortunately at this point the server dos not reply with an access token but with a 400 Bad Request and in the response body writes "{"error_description":"Error occurred while decoding public certificate of Identity Provider default for tenant domain carbon.super","error":"invalid_grant"}"
I can't understand if the problem is the JWT Token that I pass to the server or if there is some issues with the certificates. Any help please?
Okay. I think this should help you.
If you take the OpenID connect token you got from WSO2 Identity Server and put it in jwt.io you would see that the openID connect will have the "iss" (issuer) value of "https://localhost:9443/oauth2/token" (replace localhost with your hostname if you have set the hostname) which is the token endpoint of WSO2 Identity Server.
So when you use this token as a JWT Bearer grant, in order to validate the signature the grant handler tries to retrieve an IDP with the name given in the issuer(iss) field (ie. token endpoint of WSO2 Identity Server). As it fails to find any identity provider it retrieves the default identity provider (note this is a dummy IDP added for sake of backward compatibility) which doesn't have any certificate. (you can find this under $IS_HOME/repository/conf/identity/identity-providers/default.xml)
So there are two ways to fix this,
Change the issuer value of Identity Server so that it can fetch the certifcate of it's resident identity provider.
To do this,
Login to the management console,
Go to Identity Provider --> Resident --> Inbound Authentication Configuration --> OAuth2/OpenID Connect Configuration --> set the
'Identity Provider Entity Id' value as LOCAL
Create a new Identity Provider with the name equal issuer value of the OpenID connect token (ie. the token endpoint) and upload the public certificate that could be used to verify the OpenID connect token.
Personally, I prefer the first solution :)
Update:
You also need to do one more change,
In identity.xml (found under repository/conf/identity) uncomment the following lines
<Audiences>
<Audience>
${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token
</Audience>
</Audiences>
This will make sure that the audience validation check will pass for the issued OpenID connect token when used as a JWT bearer grant. (ie. the JWT Grant handler will validate whether the token endpoint is one of the audiences in the provided grant)
Is it possible to exchange an OAuth2 access token (or OpenID Connect id_token) for a WS-* SAML token?
Here is our specific scenario that we would like to accomplish:
A user has been authenticated using an OpenID Connect endpoint and issued an id_token.
The same user has been authorized using an OAuth 2 endpoint and issued an access token.
A single-page application (SPA) requests data from a secured ASP.NET Web API and it sends the id_token and access token.
Here's the question/tricky part: We would like the ASP.NET Web API to fetch data from a WCF service that is secured using WS-*, so the WCF service requires a signed SAML token.
Is it possible to exchange the OpenID Connect id_token and/or the OAuth 2 access token for a SAML token that conforms to WS-* specifications?
We would like to use ADFS on Windows Server 2016, but we're also open to other secure token services (STS), such as Azure ADFS, etc.
It seems that you could implement access token exchange in your OAuth server as there is nothing in the spec strictly forbidding it.
OAuth doesn't make any explicit specifications for what shape your access token or refresh tokens are in. So you could use WS-* or whatever suits your client/RP needs.
You could use any of these types of tokens:
WS-Security tokens, especially SAML tokens
JWT tokens
Custom tokens
The id_token itself MUST be a JWT, however.