Our OAuth server will be separate from the servers handling actual protected resources, in essence sitting in the middle of a few different systems that each hold protected resources.
For example the login page will be in one of the systems.
What should be the procedure when an authenticated user in one system goes to the other system? Since it is authenticated it should just go through, but what does OAuth do, and ask for, in this scenario?
Thank you
OAuth doesn't explicitly say anything about this scenario. But from an API design perspective I would:
Have ONE API endpoint with the different services specified in the path. Such as http://api.example.com/service1, http://api.example.com/service2 etc.
Have that one endpoint handle OAuth and querying of the separate services in the background.
If you go for having transparent "separate systems", such as http://service1.example.com and http://service2.example.com you can. But make sure to have ONE domain for handling the entire OAuth flow and that each of the API endpoints is capable of handling OAuth requests and have access to the necessary user and token databases to verify requests.
To give you an example, you can:
Receive an OAuth request to http://service1.example.com.
If service1 has access to the user and token databases you can verify the request immediately.
Or you can forward the request to your OAuth service for verification.
And the proceed with serving a response
Or my alternative (which I think is better):
Have ONE API endpoint that handles OAuth
And after verification calls the different services in the background.
Related
Yohoho! I am building an application that leverages OAuth to pull user data from provider APIs, and am wondering about the RFC compliance of the flow I have chosen.
Currently, the user signs into the authorization server which sends an auth code to my frontend client. The frontend then passes the auth code to my backend, which exchanges it for an auth token and then makes calls to the provider to pull data.
In this best practices document, it states:
Note: although PKCE so far was recommended as a mechanism to protect
native apps, this advice applies to all kinds of OAuth clients,
including web applications.
To my understanding, PKCE is designed to ensure the token is granted to the same entity that requested the auth code, in order to prevent attackers from using stolen auth codes to execute unwarranted requests.
Now, it makes sense to me why this is important even if the backend keeps the client secret unexposed, since the attacker can make requests to the backend with the intercepted auth code to receive the token. However in my flow, since I am not creating an authentication scheme and rather trying to authorize premeditated requests, the token stays with the backend.
So why is PKCE recommended here? It seems to me the most a stolen auth code can do is initiate an API request from the backend, with no token or data being returned to the attacker. And assuming a PKCE implementation is the way to go, how exactly would it work? The frontend requesting the auth code and the backend trading it for a token aren't exactly the same, so would it be as simple as passing the code_verifier to the backend to make the call?
Some clarification would be greatly appreciated.
PKCE ensures that the party who started the login is also completing it, and there are two main variations that I'll summarise below in terms of Single Page Apps (SPA).
PUBLIC CLIENTS
Consider a Single Page App that runs a code flow implemented only in Javascript. This would store a code verifier in session storage during the OpenID Connect redirect. Upon return to the app after login, this would be sent, along with the authorization code and state to the Authorization Server.
This would return tokens to the browser. If there was a Cross Site Scripting vulnerability, the flow could be abused. In particular the malicious code could spin up a hidden iframe and use prompt=none to get tokens silently.
CONFIDENTIAL CLIENTS
Therefore the current best practice for Single Page Apps is to use a Backend for Frontend (BFF), and never return tokens to the browser. In this model it is more natural for the BFF to operate like a traditional OpenID Connect website, where both the state and code_verifier are stored in a login cookie that lasts for the duration of the sign-in process.
If there was a Cross Site Scripting vulnerability, then session riding is possible by the malicious code, to send the authorization code to the BFF and complete a login. However, this would just result in writing secure cookies that the browser cannot access. Similarly, the hidden iframe hack would also only rewrite cookies.
The code_verifier could alternatively be stored in session storage and sent from the browser to the BFF, but it would be easy enough for malicious code to grab it and also send it to the server. This does not really change the risks, and the key point is that no tokens should be returned to the browser. It is OK to use secondary security values in the browser, as long as you can justify them, eg for security reviewers. Generally though it is easier to explain if secure values are in secure cookies and less visible to Javascript.
FURTHER INFO
Best practices often vary depending on the customer use case, and at Curity we provide resources for customers (and general readers) to explain secure design patterns. These are based on security standards and we translate them to customer use cases. You may
find our recent SPA Security Whitepaper useful.
I've got an OAuth2-driven authorization system for the applications in my ecosystem using the Auth Code flow. At the moment, it is working extremely well following what I feel are best practices. My current flow can be summed up as this:
User clicks a Login button in application frontend.
Backend for application redirects user to Auth Server login page with redirect URI, client ID, etc, in params.
User logs in, Auth Server redirects to Backend /authcode endpoint with Authorization Code.
Backend authenticates with Authorization Code, receives Access (JWT) & Refresh tokens. It stores Refresh token in its own database, and returns Access token as an HTTP-Only cookie.
When Access token expires, Backend sends Refresh token to Auth Server to refresh.
The problem with this approach is it is heavily dependent on a single-backend/monolith/etc architecture. When the access token expires, the backend app in question needs to be able to get the refresh token and then do the refresh against the Auth Server.
My goals for the future state of this architecture are driven by two main concerns:
Allowing backends to be split into multiple micro-services that are all capable of authenticating the same JWT.
Supporting SSO. Currently, even though all users are managed through the same Auth Server and use the same credentials, they need to enter those credentials separately for each app they log into.
My initial thought was to move the refresh handling to the client-side part of the architecture. Since all the apps are owned by me, I could setup a new flow where a user would login directly with the Auth Server, and then the Access (JWT) and Refresh tokens are set as HTTP Only cookies. This is further supported by the fact that all of my apps are hosted via a single domain name (with different root URI paths for different apps). The new token structure would determine which apps a given user has access to.
My concern there is how to secure the Refresh Token client-side. Most likely I would need to use the Path attribute and depend on the fact that all my apps are on the same hostname, which as I said they are and that will not be changing.
I guess the point of this post is I'm looking for guidance on best practices for handling this kind of scenario.
A few thoughts based on design patterns we recommend at Curity:
BACKENDS AND JWTs
Related APIs should be able to forward JWT access tokens to each other, as detailed in the scopes article. Validating the JWT in each API results in a recommended zero trust setup.
BACK END FOR FRONT END
We recommend a particular way of doing this, focused on separation of web and API concerns - see this code example on the token handler pattern.
Token refresh is handled via HTTP Only cookies and client side retries. This provides a stateless solution that is easy to manage, and where the web back end is static content only, eg a content delivery network. It requires a couple utility API components to be deployed though.
An alternative option, as you say, is to write code within your back end to store tokens, so that cookies only contain a Session ID.
COOKIES AND MULTIPLE WEB APPS
In our resources we use reverse proxies / API gateways as the entry point to APIs, rather than a web back end. Common plumbing such as translating cookies to tokens can then be managed via gateway plugins - as covered in this tutorial.
However, when you have multiple web apps you need to keep cookies isolated during API requests. Each app therefore needs its own API routes. This is awkward but is a consequence of needing to use secure cookies for best browser security. It is best managed in the gateway, and separate domains or subdomains is usually cleanest:
https://api.brand1.com
https://api.brand2.com
Each web team is then responsible for their own API routes and the cookie / CORS / CSRF stuff, rather than the API developers.
SSO
This should be determined solely by the Identity Provider session cookie, which will remain valid as you navigate across apps. If you are presenting a login button when the app does not have a secure cookie yet, then it will not feel like SSO though. One technique for making SSO look seamless is for each app to automatically redirect when it does not have application cookies yet, rather than presenting a login button.
SUMMARY
A long answer, but the best solutions require separation of concerns that is not always obvious. Done well, the end result should be simple code in apps, correct security and an architecture that can be scaled.
I have a scenario in which a mobile (native) app is requiring data from a long running task. The task is a continuesly running on the users behalf, and we have decided to move it to a backend service. To execute the task, the backend service must regularly fetch data from an API that is OAuth2 protected.
Our dilemma is that we are not sure how to provide the backend service with a set of access tokens to access the data API. Our mobile app uses the OAuth2 Authorization Grant flow with PKCS to get its own access token, refresh token and id-token (we use OpenID Connect). But how do we provide our backend service with a set of tokens? As the backend is continuesly running (also in absence of the mobile native app) we would like to provide it with its own set of access/refresh tokens.
There seem to be several solutions:
Proxy all mobile app communication to external APIs via the backend, make the client a private client, use the authorization code flow, and setup custom login sessions between the native app and our backend. To me this means more running backend infrastructure, and implementing session management including session refresh, etc. which is like re-implementing the OpenId flows on my own servers...
Using OpenID hybrid flow, providing both an access token as well as an authorization code to be shared with the backend. However, this seems to be directed at (in-browser) web-apps, not so much native mobile apps as it based on the implicit flow and as such less secure.
Doing the authorization code flow twice on the mobile app (optionally second time with prompt=none to suppress user interaction), keeping one code for the app, forwarding the second to the backend. Then both backend and app can exchange their own codes for access tokens. This feels a bit like a hack as the backend should be a private client, not a public client. It is the approach that Google seems to advertise though in CrossClientAuth
Performing impersonation by sending the id-token to the backend which then exchanges that id-token for its own set of access/refresh tokens. Microsoft does this through the jwt-bearer grant in its "on-behalf-of" scenarios. Moreover, the token-exchange RFC seems to cover the same use-case. However, in this scenario the id-token is indirectly used as access credential, not just as a bag of claims about the user identity, which is odd in my view as access should be controlled through access tokens, is it not? Moreover, not all services should be allowed to impersonate, so I suppose this comes with additional configuration complexity.
To me it feels that I'm overlooking something, this must be a problem others have too and must have been solved before... What would be best practice in my situation?
For the past 10+ days I've read an watched ALL the content I could find on understanding OAuth2 and OpenID Connect, only to find that many people disagree on the implementation, which really confuses me.
To my understanding, all the articles and examples I found assume you want access to eg. google calendar, profile info or emails if you eg. login with google, but I do NOT need to access other than my own API's - I only want to use Google, Facebook etc for logging in, and getting an id which I can link to my user in my own database - nothing more than that.
I'll try illustrate my use case and use that as an example.
A note on the diagram: the Authentication service could probably be built into the API Gateway - not that i matters for this example, since this is not about "where to do it", but "how to do it the best way" possible, for an architecture such as mine, where it's used for my own API's / Microservices, and not accessing Google, Facebook etc. external API's
If you can understand what I'm trying to illustrate with this diagram above, please tell me if I've misunderstood this.
The most basic requirements for this architecture you see here are:
Users can login with Google, Facebook, etc.
The same login will be used for all micro-services
OpenId user will have a linked account in the database
User access is defined in my own db, based on groups, roles and permissions
I do not intend to use external API's after the user is authenticated and logged in. No need for ever accessing a users calendar, email etc. so I really just need the authentication part and nothing else (proof of successful login). All user access is defined in my own database.
So a few fundamental questions comes to mind.
First of all, is OpenID Connect even the right tool for the job for authentication only (I'll have no use for authorization, since I will not need read/write access to google / facebook API's other than getting the ID from authenticating)?
People generally do not agree on whether to use the ID or Access token for accessing your own API's. As far as I understand the ID token is for the client (user-agent) only, and the access token is for eg. accessing google calendar, emails etc.... External API's of the OpenID Provider... but since I'll only be accessing my own API's, do I event need the access token or the ID token - what is the correct way to protect your own API's?
If the ID token is really just for the client, so it can show eg. currently logged in user, without going to the DB, I have 0 use for it, since I'll probably query the user from from the db and store it in redux for my react frontend app.
Dilemma: To store user details, groups, roles and permission inside JWT or not for API authorization?
By only storing the user identifier in the token, it means that I always allow authenticated users that has a valid token, to call endpoints BEFORE authorization and first then determine access based on the db query result and the permissions in my own database.
By storing more data about the user inside the JWT, it means that in some cases, I'd be able to do the authorization / access (group, role, permission) check before hitting the API - only possible with user info, groups, roles and permission stored inside a JWT issued upon login. In some cases it would not be possible due to eg. the CMS content access permissions being on a per-node level. But still it would mean a little better performance.
As you can see on the diagram I'm sending all API requests through the gateway, which will (in itself or with an authentication service) translate the opaque access token into some JWT with an identifier, so I can identify the user in the graph database - and then verify if the user has the required groups, roles and permissions - not from an external API, but from my own database like you see on the diagram.
This seems like a lot of work on every request, even if the services can share the JWT in case multiple services should need to cross call each other.
The advantage of always looking up the user, and his permissions in the db, is naturally that the moment the user access levels change, he is denied/granted access immediately and it will always be in sync. If I store the user details, groups, roles and permission inside a JWT and persist that in the client localstorage, I guess it could pose a security issue right, and it would be pretty hard to update the user info, groups, roles and permissions inside that JWT?
One big advantage of storing user access levels and info inside the JWT is of course that in many cases I'd be able to block the user from calling certain API's, instead of having to determine access after a db lookup.
So the whole token translation thing means increased security at the cost of performance, but is is generally recommended and worth it? Or is it safe enough to store user info and groups, roles, permissions inside the JWT?
If yes, do I store all that information from my own DB in the ID Token, Access token or a 3rd token - what token is sent to the API and determines if the user should be granted access to a given resource based on his permissions in the db? Do I really need an access token if I don't need to interact with the ID providers API? Or do I store and append all my groups, roles, permissions inside the ID token (that doesn't seem clean to me) issued by OpenID connect, and call the API and authorize my own API endpoints using that, even if some say you should never use the ID token to access an API? Or do I create a new JWT to store all the info fetched from my database, which is to be used for deciding if the user can access a given resource / API endpoint?
Please do not just link to general specs or general info, since I've already read it all - I just failed to understand how to apply all that info to my actual use case (the diagram above). Try to please be as concrete as possible.
Made another attempt to try and simply the flow:
The following answer does only apply for a OpenID Connect authentication flow with a 3rd party IDP (like Google). It does not apply for an architecture where you host your own IDP.
(There are some API gateways (e.g Tyk or Kong) which support OpenID Connect out of the box.)
You can use JWTs (ID token) to secure your APIs. However, this has one disadvantage. JWTs cannot be revoked easily.
I would not recommend this. Instead you should implement an OAuth2 authorization server which issues access tokens for your API. (In this case, you have two OAuth2 flows. One for authentication and one for authorization. The ID and access token from the IDP are used only for authentication.)
The following picture shows a setup where the API gateway and authentication/authorization server are two separate services. (As mentioned above, the authentication/authorization can also be done by the API gateway.)
The authentication flow (Authorization Code Grant) calls are marked blue. The authorization flow (Implicit Grant) calls are marked green.
1: Your web app is loaded from the app server.
2a: The user clicks on your login button, your web app builds the authorization URL and opens it. (See: Authorization Request)
2b: Because the user hasn't authenticated and has no valid session with your authorization server, the URL he wanted to access is stored and your authorization server responds with a redirect to its login page.
3: The login page is loaded from your authorization server.
4a: The user clicks on "Login with ...".
4b: Your authorization server builds the IDP authorization URL and responds with a redirect to it. (See: Authentication Request)
5a: The IDP authorization URL is opend.
5b: Because the user hasn't authenticated and has no valid session with the IDP, the URL he wanted to access is stored and the IDP responds with a redirect to its login page.
6: The login page is loaded from the IDP.
7a: The user fills in his credentials and clicks on the login button.
7b: The IDP checks the credentials, creates a new session and responds with a redirect to the stored URL.
8a: The IDP authorization URL is opend again.
(The approval steps are ignored here for simplicity.)
8b: The IDP creates an authorization and responds with a redirect to the callback URL of your authorization server. (See: Authentication Response)
9a: The callback URL is opened.
9b: Your authorization server extracts the authorization code from the callback URL.
10a: Your authorization server calls the IDP's token endpoint, gets an ID and access token and validates the data in the ID token. (See: Token Request)
(10b: Your authorization server calls the IDP's user info endpoint if some needed claims aren't available in the ID token.)
11a/b: Your authorization server queries/creates the user in your service/DB, creates a new session and responds with a redirect to the stored URL.
12a: The authorization URL is opend again.
(The approval steps are ignored here for simplicity.)
12b/+13a/b: Your authorization server creates/gets the authorization (creates access token) and responds with a redirect to the callback URL of your web app. (See: Access Token Response)
14a: The callback URL is opened.
14b: Your web app extracts the access token from the callback URL.
15: Your web app makes an API call.
16/17/18: The API gateway checks the access token, exchanges the access token with an JWT (which contains user infos, ...) and forwards the call.
A setup where the authorization server calls the API gateway is also possible. In this case, after the authorization is done, the authorization server passes the access token and JWT to the API gateway. Here, however, everytime the user infos change the authorization server has to "inform" the API gateway.
This is a very long question. But I believe most can be summarised by answering below,
To my understanding, all the articles and examples I found assume you want access to eg. google calendar, profile info or emails if you eg. login with google,
You do not necessarily use Access token (ID token in some occasions) to access the services offered by token issuer.You can consume tokens by your own APIs. What these Identity Providers (synonym to Authorization server, or IDP in shorthand) is to hold identities of end users. For example, typical internet have a Facebook account. With OAuth and OpenID Connect, the same user get the ability to consume your API or any OAuth/OIDC accepted service. This reduce user profile creation for end users.
In corporate domain, OAuth and OIDC serves the same purpose. Having a single Azure AD account lets you to consume MS Word as well as Azure AD's OIDC will issue tokens which can be used to Authorise against an in-house API or an third party ERP product (used in organization) which support OIDC based authentication. Hope it's clear now
A note on the diagram is that the Authentication service could probably be built into the API Gateway - not sure if that would be better?
If you are planning to implement an API gateway, think twice. If things are small scale and if you think you can maintain it, then go ahead. But consider about API managers which could provide most of your required functionalities. I welcome you to read this article about WSO2 API manger and understand its capabilities (No I'm not working for them).
For example, that API manager has built in authentication handling mechanism for OAuth and OIDC. It can handle API authentication with simple set of configurations. With such solution you get rid of the requirement of implement everything.
What if you can't use an API manager and has to do it yourself
OpenID Connect is for authentication. Your application can validate the id token and authenticate end user. To access APIs through API Gateway, I think you should utilise Access token.
To validate the access token, you can use introspection endpoint of the identity provider. And to get user information, you can use user-info endpoint.
Once access token is validated, API gateway could create a session for a limited time (ideally to be less or equal to access token lifetime). Consequent requests should come with this session to accept by API gateway. Alternatively, you can still use validated access token. Since you validated it at the first call, you may cache for a certain time period thus avoiding round trips to validations.
To validate user details, permission and other grants, well you must wither bind user to a session or else associate user to access token from API gateway at token validation. I'm also not super clear about this as I have no idea on how your DB logic works.
First Appreciate your patience in writing a very valuable question in this forum
we too have same situation and problem
I want to go through ,as images are blocked in our company in detail
Was trying to draw paralles to similar one quoted in the book
Advance API In Practise - Prabath Siriwerdena [ page 269]Federating access to API's Chapter. Definitely worth reading of his works
API GW should invoke Token Exchange OAUTH2.0 Profile to IDP [ provided the IDP should support TOken Exchange profile for OAUTH 2.0
The Absence of API Gateway , will result in quite bespoke development
for Access Control for each API Check
you land up in having this check at each of the api or microservice [ either as library which does work for you as a reusable code]
definitely will become a choking point.]
I'm trying to implement authentication/authorization in my solution. I have a bunch of backend services(including identity service) under API Gateway, "backend for frontend" service, and SPA (React + Redux). I have read about OAuth2.0/OpenIdConnect, and I can't understand, why I shouldn't use Resource owner password flow?
A client ( my backend for frontend server ) is absolutely trusted, I can simply send users login/password to the server, then it forwards them to Identity server, receives the access token && refresh token and stores refresh token in memory(session, Redis, etc), and send the access token to SPA, which stores it in local storage. If SPA will send a request with the expired access token, the server will request a new one using refresh token and forwards the request to API Gateway with the new access token.
I think in my case a flows with redirects can provide worth user experience, and are too complicated.
What have I misunderstood? What potholes I'll hit if I'll implement authentication/authorization as I described above?
OAuth 2.0 specification's introduction section gives one key information on the problem it tries to solve. I have highlighted a section below,
In the traditional client-server authentication model, the client
requests an access-restricted resource (protected resource) on the
server by authenticating with the server using the resource owner's
credentials. In order to provide third-party applications access to
restricted resources, the resource owner shares its credentials with
the third party
As a summary what OAuth wants to provide is an authorization layer which removes the requirement of exposing end user credentials to a third party. To achieve this it presents several flows (ex:- Authorization code flow, Implicit flow etc.) to obtain tokens which are good enough to access protected resources.
But not all clients may able to adopt those flows. And this is the reason OAuth spec introduce ROPF. This is highlighted from following extraction,
The resource owner password credentials grant type is suitable in
cases where the resource owner has a trust relationship with the
client, such as the device operating system or a highly privileged
application.The authorization server should take special care when
enabling this grant type and only allow it when other flows are not
viable.
According to your explanation, you have a trust relationship with client. And your flow seems to be work fine. But from my end I see following issues.
Trust
The trust is between end user and the client application. When you release and use this as a product, will your end users trust your client and share their credentials.? For example, if your identity server is Azure AD, will end users share Azure credentials with your client.?
Trust may be not an issue if you are using a single identity server and it will be the only one you will ever use. Which brings us the next problem,
Support for multiple identity servers
One advantage you get with OAuth 2 and OpenID Connect is the ability to use multiple identity servers. For example, you may move between Azure AD, Identityserver or other identity servers which of customer's choice (ex:- they already use on internally and they want your app to use it). Now if your application wants to consume such identity servers, end users will have to share credentials with your client. Sometimes, these identity servers may not even support ROPF flow. And yet again TRUST become an issue.!
A solution ?
Well I see one good flow you can use. You have one front end server and a back-end server. I believe your client is the combination of both. If that's the case you could try to adopt authorization code flow. It's true your front end is a SPA. But you have a backend you can utilise to obtain tokens. Only challenge is to connect front end SPA with back end for token response (pass access token to SPA and store other tokens in back-end). With that approach, you avoid above mentioned issues.