OpenId Connect renew access_token in SPA - asp.net-core

Trying to implement OpenId Connect in Web Application consisting of following components
Identity Provider
Resource server
Single Page Application acting as Client.
Identity Provider and Resource Server are the same application.
SPA use Password Flow to get access_token and stores into the cookie. Storing access_token into cookie has it's security threads, but's it's a different story.
Problem
access_token issued by IdP is expired after 30 min and SPA needs to renew token without asking users for credentials again.
Solution
IdP returns refresh_token along with access_token. Whenever SPA gets 401 from Resource Server, it sends refresh_token to IdP and get's new access_token back.
Problem
Sending refresh_token to SPA is bad practice.
A Single Page Application (normally implementing Implicit Grant) should not under any circumstances get a Refresh Token. The reason for that is the sensitivity of this piece of information. You can think of it as user credentials since a Refresh Token allows a user to remain authenticated essentially forever. Therefore you cannot have this information in a browser, it must be stored securely.
Suggested solution
When the Access Token has expired, silent authentication can be used to retrieve a new one without user interaction, assuming the user's SSO session has not expired.
I think Silent Authentication is not applicable to Password Flow when IdP and Resource Server is same application. access_token issued by IdP is only piece of information which can be used to authorize against Resource Server/IdP after its expiration, how a client can convince IdP to issue new access_token? (without sending refresh_token)
Found angular-oauth2-oidc library which uses refresh_token to renew access_token.
What is best practice/solution in this case to renew access_token?
technical details
Identity Provider - ASP.NET Core + Openiddict library.
SPA - AngularJs application.

Single page applications must not receive refresh tokens. That has been established rules in OAuth 2.0 and OpenID Connect.
One good option I see here is to use Implicit Flow. This will establish a front channel session from your browser to Identity Provider. With password grant type you do a back-channel call (POST), so you don't get such session.
Usually this is a cookie which points to information about previous logged in status (these are identity provider specifics). With completion of the flow, SPA will receive the access token. As you figured out, it will expire. But once that happens, SPA can trigger another implicit flow, but this time with prompt query parameter.
prompt
Space delimited, case sensitive list of ASCII string values that
specifies whether the Authorization Server prompts the End-User for
reauthentication and consent. The defined values are: none , login, consent and select_account
If you identity provider maintain a long lived session (ex:- few hours or days) or if it maintain a remember me cookie, SPA could use prompt=none making it to skip login step from identity provider. Basically, you are getting browser based SSO behaviour with this.

Using the Resource Owner Password Credentials flow defeats the refresh token storage argument: instead of not being able to store the refresh token in a secure place, the SPA would now have to store the Resource Owner credentials in a secure place (assuming you want to avoid requesting username/password from the user frequently). The Implicit grant was designed for usage with an SPA, so it is better to stick with that.

Further to previous answers, the latest OAuth working group guidance for SPAs no longer recommends use of the implicit flow.
If you have simple, shared domain app (IdP, RS and client on a single domain) then you should consider not using OAuth at all. From the doc:
OAuth and OpenID Connect provide very little benefit in this
deployment scenario, so it is recommended to reconsider whether you
need OAuth or OpenID Connect at all in this case. Session
authentication has the benefit of having fewer moving parts and fewer
attack vectors. OAuth and OpenID Connect were created primarily for
third-party or federated access to APIs, so may not be the best
solution in a same-domain scenario.
If you are using OIDC/OAuth in a SPA, they recommend the auth code flow with PKCE.

Related

Separate authentication and authorization servers for SPA app

I have
UI (a single page app)
an external authentication server
my own authorization server
resource server (my own backend APIs)
Here's what I am trying to do
UI/User gets an AuthN token from the external authentication server.
UI sends the AuthN token to get the an AuthZ token from my own authorization server
UI uses the AuthZ token to retrieve data from the resource server
But the problem is I don't know if the user is still authenticated anymore because I stopped using the AuthN token from step 3. Should I use both tokens together? or somehow consolidate the 2 tokens into one? Hope to get some ideas from here. Thanks!!
COMPONENTS
This is the standard way of managing components:
UI makes an OpenID Connect redirect to the Authorization Server (AS)
AS makes a second OpenID Connect redirect to the authentication system. There could be more than one of these, eg Google, Facebook.
After user sign in the AS issues the same tokens for your UI and resource server, regardless of how the user signs in. The UI sends access tokens to the resource server which can authorize based on scopes and claims received.
Unless you have special reasons, do not use foreign tokens from authentication systems in your own applications. This is because you are not in a position to control their contents.
OPENID CONNECT RE-AUTHENTICATION MECHANISMS
The OpenID Connect prompt and max-age parameters can be used to control how frequently the user is prompted to re-authenticate, and the auth_time claim can be issued in ID tokens to inform the UI of the last authentication time.
For example your app could use access tokens that last 15 minutes. Whenever they expire you could send a request with a prompt-none parameter to see if the user is still authenticated. If not then you will receive a login_required response and you could then redirect the user to re-authenticate.
SINGLE LOGOUT
Knowing if the user is still authenticated suggests you need to know if they signed out in another app. OpenID Connect has four Single Logout Mechanisms that you should be aware of, and which may possibly work for your scenario.
This is a technical area that has never worked perfectly in any Single Sign On technology though. This may be because you do not control all apps, or because of technical limitations, eg Google may not inform the Authorization Server if the user signs out of Gmail.
SUMMARY
Your apps should only use the authorization server tokens. Use OIDC request parameters to control when the user must re-authenticate.

Where to store tokens obtained through OAuth2 (OpenID Connect)

I build an application that uses some Identity Provider (Auth0, FusionAuth, Azure AD, AWS Cognito) on user's choice.
I'm using Authorization Code flow.
And i faced issue with storing tokens.
I want to use Identity Provider to only user's sign in, i do not need to store who is the user (name, email etc.). I just need to authenticate user and be sure that user come from Identity Provider that i can trust.
So i have 4 questions:
Do i need to store tokens if i am not consider to use them to make requests to the Identity Provider.
If no goto 4 question. If yes i want to know what the best way to store them (save in session with db or just send them as accessToken for Header and refreshToken in cookie)
How i can validate access token, because as i know access token may or may not be JWT. Make request to IP on each request is not the best way, because of amount of requests.
Should i use my own created pair of access and refresh tokens to validate requests from Front End.
I'm only recently started investigate OAuth2 and will appreciate any answers.
As of now i have this flow:
From Front End(FE) user redirects to the Identity Provider(IP) to sign in.
IP redirects to the Back End(BE) with code.
BE make request to IP to obtain tokens.
BE validates that authorization is valid (via nonce and state).
BE redirects user to the FE with refreshToken in the httpOnly secure cookie and accessToken in query to store it in localStorage.
When FE make request to BE i validate accessToken(JWT) using jwks.
In OAuth you should not generally have to implement plumbing to build your own token stores. Eg tokens can be stored in strongly encrypted HTTP only SameSite=strict cookies. However you have to stay within cookie size limits. The best way to do this is to issue opaque tokens (such as UUIDs) to internet clients. The Phantom Token Pattern has more info on this.
You should not use foreign access tokens in your own APIs. As you are discovering, you may not be able to validate them. Also they will not have meaningful scopes and claims and you will not be able to authorize API requests properly. Instead issue your own tokens for your own APIs.
AUTHORIZATION SERVER
It is possible to issue your own tokens in code but this is not recommended. Instead, the preferred option is to use an Authorization Server. One option is the free community edition of the Curity Identity Server.
This component will take care of connections to Identity Providers for you. It will then issue tokens for you, so that your apps and APIs only ever deal with tokens from a single provider.

When an Oauth provider is used to delegate authentication, then does the appserver return any token to the caller?

In Oauth and Openidconnect, the appserver endpoint invocation starts the Oauth flow and the app server gets the token from the auth server and is able to pass the token to resource server to access resources (delegation) on behalf of the resource owner.
The token exchange happens between the app server and resource server, and the token never arrives at the end users browser.
I'm working on a web api (aka app server) that will be consumed by a mobile app. There is no other server involved. Presently the login endpoint returns a signed JWT token to the user if correct credentials are supplied (validate against the db). User places this token in the header of the subsequent request.
Assuming I don't want to have a user db and validate logins, and instead delegate the auth check to another service like azure b2c or firebase (that use the Oauth), then I assume the flow is like given below:
Api registered the firebase/azure b2c (let's call this the provier) clientid, secret.
User invokes login endpoint of my api
The api invokes the provider's Oauth flow.
User gets popup to authenticate with the provider.
Eventually the provider will send the token (containing the claim like username) to the api (aka app server)
Does the user get back any token? Otherwise, when the user makes subsequent endpoint calls, then how is the endpoint able to identify who is this user and whether he is already authenticated?
Although it is okay to send back the access token to the user , but from the security best practise 's point of view , it is better not to do it which I quote the reasons as follow from this:
Because of the issues outlined above, the best security recommendation
for an SPA is to avoid keeping tokens in the browser at all. This can
be achieved with the help of a lightweight back-end component, often
described as a Backend-For-Frontend.
The backend component can then be configured as a confidential OAuth
client and used to keep tokens away from the browser. It can either be
stateful and keep tokens in custom storage, or stateless and store the
tokens in encrypted HTTP-only, same-site cookies. Whichever variant is
chosen, the backend component creates a session for the SPA, using
HTTP-only, secure, same-site cookies, thus enabling a high level of
security. Such cookies cannot be read by scripts and are limited to
the domain of the SPA. When combined with strict Content Security
Policy headers, such architecture can provide a robust protection
against stealing tokens
Also from here ,they suggest for mobile app 's OAuth2 best practise , it should perform the OAuth flow inside a system browser component.

How do I keep the user logged-in with Implicit flow?

From what I understand, the end-result of the implicit flow is the access token, which allows the client (in my case a JS SPA) to authenticate into resource servers (APIs).
The access token is usually only valid for ~1 hour, then it expires - making it useless.
What should my JS app do then? Redirecting the user back to the auth server is unrealistic since then the user will have to reenter their credentials every 1 hour!
I also know that the implicit flow doesn't support refresh tokens so I can't use those either.
Is there a way to persist the user's login? How do things like Facebook keep you logged-in indefinitely?
Just to clarify, you are asking about the Implicit flow which is detailed in the OAuth 2.0 RFC rather than OpenID Connect which deals more with authentication of a user?
With the implicit flow you do have to regularly call the authorisation endpoint to obtain a new token, but if the user remains logged into their identity provider then they should not be prompted to resubmit their credentials, and the token will be returned as a hash fragment in the redirect uri, with no user interaction required.
You can use an AJAX call to get the token on a back-channel so your SPA app user experience is not affected by the need to get new tokens.
To address the points you highlight in your question:
The access token is usually only valid for ~1 hour, then it expires -
making it useless.
Correct!
then the user will have to reenter their credentials every 1 hour!
Not necessarily.
If the user stays logged into the identity provider (e.g. facebook, google) then there will be a browser cookie between the user and that provider. This effectively means the identity provider does not need the user to re-enter credentials. The authorisation server should be able to return you a token with no interaction required.
Is there a way to persist the user's login?
You can't control it from your SPA. It's totally dependent on the user staying logged onto the identity provider. If they stay logged into facebook, google (or whatever IDP you app uses) then you should be able to get tokens non-interactively.
This article nicely explains how the implicit flow can be implemented.
If the session at the OP is still active (via a cookie perhaps), then OpenID Connect has a mechanism to refresh tokens in a (hidden) iframe: prompt=none.
According to the spec, when sending this flow...
The Authorization Server MUST NOT display any authentication or consent user interface pages. An error is returned if an End-User is not already authenticated or the Client does not have pre-configured consent for the requested Claims or does not fulfill other conditions for processing the request. The error code will typically be login_required, interaction_required, or another code defined in Section 3.1.2.6. This can be used as a method to check for existing authentication and/or consent.
prompt=none is also referred to from the Session Management specification.

What type of token/auth to use for non-interactive API clients in an OIDC context?

We consider using OpenID Connect with ID tokens for authentication of our public API.
These are the usage scenarios we'd like to cover:
Web UI (single page, client-side JavaScript app)
Command line interface (CLI) used in an interactive session
CLI used non-interactively, e. g. in a CI/CD pipeline
Other API calls executed in a non-interactive session
The idea for (1) and (2) is to use the OIDC implicit grant type, so that the user authenticates interactively (username/password) at our OpenID Connect identity provider and permits the RP (relying party, client) to access the users identity. The identity provider will then issue a short-lived ID token, a refresh token and (optionally?) an access token to the RP.
For (3) and (4) an interactive authentication is out of the question. We'd instead like to issue tokens to the users which allow them to access our API on their behalf. These tokens should be long living, only invalidated when they get deleted in the system.
Still, we want to use JWT just like the ID tokens issued by the identity provider as a carrier of identity information for all API requests internally.
My questions are:
Can this be done purely with one of the tokens issued by the OpenID Connect implicit grant type?
Can an access token be issued in a long-lived (no expiry, only invalidated by deleting from the system) way and then be exchanged by the client against an ID token?
Or is the refresh token the thing to use for exactly that?
Or do we have to solve this outside OpenID Connect? Which leaves the question how to resolve opaque tokens from API requests against identity details (JWT) for use in our API/services?
If you use implicit flow (for Scenarios 1 and 2), you can't use refresh tokens. You need client credentials (client ID and secret) to request for refresh tokens. In the Implicit flow, we don't store any client credentials.
When a client is Public client (SPA,etc..), it is not safe to store client secret in it. So public clients generally use Implicit flow. Implicit flow doesn't support refresh tokens. Some of the OIDC libraries implement Silent token renewal/refresh feature to circumvent the absence of refresh tokens. But there are some limitations with that model (you need to have active session with IDP to get the renewal working without any interruption)
TL;DR -> If a client is public client, use implicit flow (which don't need client secret to get access tokens from IDP). Implicit flow doesn't support refresh tokens.
Can this be done purely with one of the tokens issued by the OpenID Connect implicit grant type?
It is not possible to use refresh tokens with implicit flow. Authorization code flow supports refresh tokens but can't be used with SPA clients. So you need a combination of OAuth 2.0/OIDC flows.
Can an access token be issued in a long-lived (no expiry, only invalidated by deleting from the system) way and then be exchanged by the client against an ID token?
These are two different things:
"Invalidated by deleting from the system" : With this we are discussing about Self-Contained tokens vs Reference tokens.
Self-Contained Tokens: These tokens contains all the information required to validate its authenticity in it - for e.g. the issuer details, its validity, etc.. A client don't need to make a back-channel call to STS to confirm the authenticity. These tokens are sometimes hard to revoke and will be valid for the duration as specified in the token.
Reference Tokens: Reference tokens are generally opaque tokens which contains a GUID like identifier in it and no other details. In order to validate the authenticity of these tokens, the client needs to make a back-channel call to STS. One main advantage is it can be easily revoked by deleting the corresponding identifier in STS DB.
"exchanged by the client against an ID token Refresh token" - I am assuming you are referring to Refresh tokens instead of ID token. We use Refresh token for this purpose
Or is the refresh token the thing to use for exactly that?
Yes. Refer to the above comments
Or do we have to solve this outside OpenID Connect? Which leaves the question how to resolve opaque tokens from API requests against identity details (JWT) for use in our API/services?
If you use opaque tokens, OIDC/OAuth 2.0 has several endpoint (like UserInfo) to get further information about the user. You can also use Introspection endpoint to know the validity of the token.
(Scenarios 3 and 4): I am not sure how you plan to use this - But for any non-interactive client(which is acting on its own and not behalf of user), you should use client credentials flow.
If the client want to act on behalf of user, you should enable a way for the user to approve this behavior.
I recommend anyone who is interested in OpenID Connect (OIDC) to look into OAuth2 specification. Since OIDC is built up-on OAuth2, it inherits many fundamental features.
First thing to note is Implicit flow does not return a refresh token.
The implicit grant type is used to obtain access tokens (it does not
support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI
If you want to rely on refresh tokens, then you MUST consider this fact.
Can this be done purely with one of the tokens issued by the OpenID Connect implicit grant type?
It depends on the design and exact requirements. But you can indeed build the authentication on top of Id token and use access token for API calls. To validate access tokens, you can use introspection endpoint from API endpoint.
Can an access token be issued in a long-lived (no expiry, only invalidated by deleting from the system) way and then be exchanged by the client against an ID token?
This could be possible depending on the configurations of identity provider expose. But by specification, this should not be done for a client who use implicit flow. And simply because of security reasons. This is the very same reason why implicit flow does not return a refresh token. On the other hand, refresh tokens are the one which can live longer. For example, Google's refresh tokens never expires (reference - 8953983).
Or is the refresh token the thing to use for exactly that?
As mentioned previously, refresh tokens can long-lived. And it can be exchanged for a fresh access token. Returning of the id token for a refresh token will depend on identity provider implementation. For example, Azure AD do return an ID token for refresh token response. But going beyond that, the identity provider can offer a user info endpoint. A good article can be found from this link
Or do we have to solve this outside OpenID Connect? Which leaves the question how to resolve opaque tokens from API requests against identity details (JWT) for use in our API/services?
ID token indeed help to authenticate the end user from client side. But when it comes to validate user from API endpoint, you can think about using introspection endpoint or a user info endpoint. But be mindful, some identity providers do not provider introspection endpoint. At the time of writing this article Azure AD do not expose one (reference - 43378748) but do provide a user info endpoint.