ADFS Client credentials grant flow with mulitple clients in an application group - oauth-2.0

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.

Related

How to obtain access token in webapp without login form (keycloak)

The goal is to get an access token from Keycloak to use that token for API calls.
The app should not require any login from the user, so it just need to authenticate itself.
I was planning to use client_credentials flow for that. However, client_credentials require the client to be confidential. And confidential clients have CORS issues in Keycloak.
If I switch to a public client I cannot use client_credentials flow anymore.
What is the best way to continue from here? Maybe I need to use password credentials flow and public client, because client credentials are going to be exposed anyway?
Create a new client, confidential, and activate service account on it.
You will be able to authenticate with grant_type=client_credentials using the client_id and client_secret as credentials (hence the service account, it's not a user account).
You will obtain an access token, no refresh token, and you'll simply have to reconnect again using the same credentials when the access_token will be expired.
For the permissions of this service account on the REST API check the 'scope' tab and the new 'Service Account' tab. You'll need access on some of the 'realm-managment' roles.
About creation new client application, it's not unusual to have several clients for the same real application, like you could have a bearer-only client for the API part, and a confidential one for other things. I do not know what is you CORS problems with confidential clients.
confidential clients are used by full stack applications (not front+back ones), and service accounts (B-to-B).
bearer_only is for API backs
public is only for front application (like angular, vuejs, etc) where you cannot store a client secret and the security depends only on the redirect_uri filter.

OAuth2 and OpenID API to API communication

We have several microservices that communicate with each other and all these microservices use Oauth2 authorization to allow access to its API. The flow starts from the UI where we use the standard 'authorization_code' flow and finally get an access_token to invoke a specific API-1 service (Registered for client_id '123'). The UI then sends a request to the API-1 (client_id 123) and our API-1 now verifies the access token passed with auth server. Once it is valid this API now wants to communicate with another API (API-2) (Our internal microservice) which needs an access_token. We cannot re-use the same access_token as it is intended for a specific client. API-1 could use a token exchange to talk to API-2 but the developers of API-1 do not wan to do any token exchange. What are the options we have in this case?
Thanks
The typical solution is to let API1 contact your token service, using its access token and exchange it using the token exchange standard, for a new access token to access API2.
See OAuth 2.0 Token Exchange and check with your token provider for details.

Authenticating using Auth2 when there are several resources

I need to implement a single sign on of a user, which can get services from several different services.
When there was only a single service, the user could log in from the client side, send the request to a backend, gets a URL back to a JWT token issuer server, from which he can get a token which he sends back to the BE and he is now authenticated.
What is now changing, is that he needs to get more services. Each service has its own frontend and backend, but everyone are using the same issuer. Meaning there are both services with FE and BE, and also there is another general BE for the authentication.
What is the correct flow to authenticate in the scenario? Can the general BE issue a token for the client for each of the required services? Or should the BE respond the client with the services's BE url and let the client itself send an authentication token response from each service? Or something else?
I assume you mean OpenID Connect, since OAuth2.0 is not used for authentication and does not require the use of JWTs. Also, in your scenario there are not multiple resources, but multiple clients / relying parties.
Using the OpendID Connect Implicit flow, the issuer will eventually send an id token (JWT) to the user's browser. This JWT can be used to authenticate to a service. Each JWT will contain an aud (audience) claim to identify the service it should be used for.
Using the Authorization Code flow, the issuer will eventually send an authorization code to the user's browser. The user will send the code to a service, and the service will send the code plus its client id to the issuer in exchange for an id token (JWT) and an access token.
In both cases, the service identifies the end user using the iss (issuer) claim, and verifies the JWT by checking the signature, expiry and audience.

Bad id_token issuer when trying to authenticate with Battle.net OIDC with Cognito

I've spent the last couple days trying to set up Cognito to use Battle.net OIDC. I believe I am most of the way there. I can see using the cognito hosted UI that it authenticates correctly but then fails, presumably trying to retrieve the token.
The first request to cognito hosted UI:
https://<removed>.auth.us-west-2.amazoncognito.com/oauth2/authorize?identity_provider=Blizzard&redirect_uri=https://<my-site-oauth-handler-removed>/oauth&response_type=CODE&client_id=<removed>&scope=openid
Then the next request is to Battle.net OIDC as expected:
https://us.battle.net/oauth/authorize?client_id=<removed>&redirect_uri=https%3A%2F%2F<removed>.auth.us-west-2.amazoncognito.com%2Foauth2%2Fidpresponse&scope=openid&response_type=code&state=<removed>
Then the code is passed onto the cognito idpresponse:
https://<removed>.auth.us-west-2.amazoncognito.com/oauth2/idpresponse?code=<removed>&state=<removed>
At this point, if my understanding is correct cognito should try to hit the battle.net /oath/token endpoint and then return the id_token and access_token to my redirect_url. It is at the point cognito instead returns an error to my url:
https://<my-site-oauth-handler-removed>/oauth?error_description=Bad+id_token+issuer+oauth.battle.net&error=invalid_request
Judging by the lack of documentation for Battle.net OIDC I may be one of a handful trying to use the battle.net OIDC with Cognito. It very well could be a bug with their implementation but I'm trying not to jump to that conclusion.
My best guess right now is that I haven't configured Cognito correctly to make the token POST request. It needs to use Basic Authentication with the clientid:password but I can't verify if it's doing that correctly or not since its abstracted away.
Anyway, when I make the request through my app (using amplify to open the hosted UI) it does return but with the following:
[ERROR] 51:05.25 OAuth - Error handling auth response. Error: Bad+id_token+issuer+oauth.battle.net
at OAuth.<anonymous> (OAuth.js:202)
at step (OAuth.js:52)
at Object.next (OAuth.js:33)
at OAuth.js:27
at tryCallTwo (core.js:45)
at doResolve (core.js:200)
at new Promise (core.js:66)
at __awaiter (OAuth.js:23)
at OAuth.handleAuthResponse (OAuth.js:181)
at AuthClass.<anonymous> (Auth.js:1632)
Here is all my relevant Cognito configuration:
OIDC Provider:
Provider Name: Blizzard
Client ID: [removed]
Client Secret: [removed]
Attributes request method: POST
Authorize scope: openid
Issuer: https://us.battle.net/oauth
App Client:
Name: [removed]
App client id: [removed]
App client secret: (no secret key)
App Client Settings:
Enabled Identity Providers: Blizzard
Callback URLs: https://[removed]/oauth
Sign out URLs: https://[removed]/oauth
Allowed OAuth Flows:
Authorization code grant
Implicit grant
Allowed OAuth Scopes
email
openid
aws.cognito.signin.user.admin
profile
(I've tried every variation of these and it doesn't appear to change the outcome)
Federated Identity:
Authentication providers
OpenID
us.battle.net/oauth
IAM Identity Providers:
Provider Name: us.battle.net/oauth
Provider Type: OIDC
Provider URL: us.battle.net/oauth
CA Thumbprint: [removed]
Audience: [my battle.net client id]
I have exhausted my own resources and am asking for any guidance with this.
Thanks!
This was caused by Blizzard issuing their tokens from their well-known endpoints but setting the iss field to oauth.battle.net which caused Cognito (or any other token validation) to fail hence the error message
Bad+id_token+issuer+oauth.battle.net
I brought this up to their API team in Discord and they are releasing a fix on November 25th according to an email send to developers.
Dear community developer,
Earlier this year, we introduced a new OAuth discovery endpoint which
implements the OpenID Connect discovery specification. We would like
to deploy a change to the issuer field iss of the id_token during the
OpenID authorization flow from oauth.battle.net to the issuer that is
returned by the well-known configuration endpoint JSON response. This
would be a potential breaking change to some of the OAuth OIDC clients
but it conforms to the OpenID connect specification:
https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier
Your OAuth client application was identified as one that could be
affected by this change.
We are planning to release this change on November 25, 2019.
Here are the steps that you can take to ensure your OpenID Connect
client will continue working after the change:
Navigate to one of the well-known configuration endpoints below which
corresponds to the region where your client application operates and
check the "issuer" field.
If the issuer field matches the configured issuer for your OAuth OIDC
client then your client is compatible and you are already compliant
and do not need to make any changes.
If the issuer field from the well-known configuration endpoint is
different than the issuer set in your client configuration, please
change the issuer to match the well-known configuration endpoint.
If your client supports the OpenID discovery endpoint standard, you
can configure it to read all necessary settings from the well-known
configuration endpoint. Your OAuth client should self-configure.

oAuth 2.0 and exposed APIs

We have a web portail using some exposed API from a service desk application to create and list tickets. oAuth 2.0 is used to authenticate the requests through Google server. Below is the roles of the differents components :
Google oAuth 2.0 : Authentication server
Service Desk application : Resource server with exposed API
Client : Web portail
Which oAuth fow i have to use to authenticate the requests from the client to the resource server ? I think it's the client credentials.
How to check when the resource server receives a request that the request is authorized and can access the resource ? Currently, the resource server checks the access token provided with the request through the authorization server Google (using the API useremail endpoint), then, the resource server checks locally in a file if the client_id is declared.
Any help will be welcome
Thanks
If you want to keep the token at the web portal, you probably want to use the Implicit flow. The Resource Owner Password Credentials flow has some problems:
It requires a client password, which cannot be stored safely in a browser,
Your web portal gets the username and password (of a portal user), which is not good from the security standpoint
You cannot handle authorization of your own application (Service Desk) using Google access tokens, because they contain only Google-related scopes (for accessing Google services and resources). You can either do it locally - have some mapping between Google users and local roles or to have your own OAuth2 server with your own scopes that accepts Google authentication (for example KeyCloak).

Resources