Sign-up/Sign-in between SPA and a REST API with OpenID Connect - rest

Consider:
An SPA or a statically generated JAMStack website (written in Vue/Nuxt).
A REST API (Node + Express) running in the cloud.
The website is being served through a CDN.
We are required to add a Social Sign-On functionality for user convenience. The API already authenticates users with a credential based JWT (access/refresh) token flow. Social sign-on will be in addition to that.
We have selected the Authorization Code Flow as the mechanism for this implementation (instead of PKCE, because we want the SPA to use the API's access tokens instead of the authorization provider's).
Our flow is as follows:
The user clicks on a button and is directed to the Identity Provider's (in our case, Google) website to prove its identity. We provide a randomly generated state token as a request parameter to prevent CSRF attacks.
On the IdP's authentication page, the user submits their credentials and if successful, the user is redirected back to our website (the SPA) with the state token and a code as url parameters.
We then match the state value with the one that we generated on our app to defeat CSRF attacks.
We send the code value to our API in an AJAX request.
The API takes this code and our client secret and exchanges them for an identity taken with the identity provider (disregarding the access tokens as we only require proof of identity).
If the user identity is verified, the API generate a pair of access and refresh token and sends them to the SPA. (If the user does not exist in our database, it is added and similarly returned tokens for our API).
NOTE: We do not store the identity token received from the IdP on the server at all.
One receiving the response, the SPA stores the access token in localStorage, (the refresh token is stored in an HTTP only cookie by the API).
All further requests to our API from the SPA are made with the access token issued by our API, once both the refresh token and access tokens expire (through logout or timeout), the user has to re-initiate the authentication flow from step 1.
I have two questions:
Assuming that all communication between the IdP and SPA, the SPA and the API, and the API and the IdP is happening over TLS, are there any security vulnerabilities present in our process?
This one perplexes me more, how do you persist the state token that we generate during the initial request to the authentication server between page redirects? (from when we leave our SPA to authenticate on IdP's webpage to when the IdP redirects us back to our SPA). Is localStorage a secure enough option? If not, would sessionStorage suffice?

For a new project I would consider using the BFF pattern to avoid dealing with tokens at all in the SPA/browser. Because there is no secure way to deal/store tokens in the browser.
Do check out these resources for more details about why this pattern is recommended for new projects:
alert‘OAuth 2 0’; // The impact of XSS on OAuth 2 0 in SPAs
Using the BFF pattern to secure SPA and #Blazor Applications
The BFF Pattern (Backend for Frontend): An Introduction

Related

How to perform user registration and authentication between a single page application and a REST API with OpenID Connect

Consider that we have:
An SPA or a statically generated JAMStack website.
A REST API.
The website is being served with nignx that also reverse proxies to our API.
--
It is required that a user should be able to register/authenticate with an identity provider (say, Google) through the OpenID Connect protocol. For the sake of simplicity, let us assume that the user has already registered with our API.
Talking about authentication using OIDC, from what I have read on the subject, the steps you take are the following:
Register the application with the IdP and receive a client id and a secret.
When the user initiates a login (with Google) request on the API ('/api/loginWithGoogle') the API sets a state variable on the request session (to prevent CSRF) and redirects the user-agent to the IdP's login page.
At this page, the user enters their credentials and if they are correct, the IdP redirects the user to the callback URL on the API callback (/api/callback).
The request received on the callback has the state parameter (which we should verify with the one we set on the session previously) and a code parameter. We exchange the code for the identity token with the authorization server/IdP (we also receive access/refresh tokens from the auth server, which we discard for now because we do not want to access any APIs on the behalf of the user).
The identity token is parsed to verify user identity against our database (maybe an email).
Assume that the identity is verified.
-- The next part is what's giving me trouble --
The documentation that I have read advises that from here we redirect the user to a URL (e.g. the profile page)and start a login session between the user agent and the API. This is fine for this specific architecture (with both the SPA/static-site being hosted on the same domain).
But how does it scale?
Say I want to move from a session based flow to a JWT based flow (for authenticating to my API).
What if a mobile application comes into the picture? How can it leverage a similar SSO functionality from my API?
NOTE: I have read a little on the PKCE mechanism for SPAs (I assume it works for JAMStack as well) and native mobile apps, but from what I gather, it is an authorization mechanism that assumes that there is no back-end in place. I can not reconcile PKCE in an authentication context when an API is involved.
Usually this is done via the following components. By separating these concerns you can ensure that flows work well for all of your apps and APIs.
BACKEND FOR FRONTEND
This is a utility API to keep tokens for the SPA out of the browser and to supply the client secret to the token service.
WEB HOST
This serves unsecured static content for the SPA. It is possible to use the BFF to do this, though a separated component allows you to serve content via a content delivery network, which some companies prefer.
TOKEN SERVICE
This does the issuing of tokens for your apps and APIs. You could use Google initially, though a more complete solution is to use your own Authorization Server (AS). This is because you will not be able to control the contents of Google access tokens when authorizating in your own APIs.
SPA CLIENT
This interacts with the Backend for Frontend during OAuth and API calls. Cookies are sent from the browser and the backend forwards tokens to APIs.
MOBILE CLIENT
This interacts with the token service and uses tokens to call APIs directly, without using a Backend for Frontend.
BUSINESS APIs
These only ever receive JWT access tokens and do not deal with any cookie concerns. APIs can be hosted in any domain.
SCALING
In order for cookies to work properly, a separate instance of the Backend for Frontend must be deployed for each SPA, where each instance runs on the same parent domain as the SPA's web origin.
UPDATE - AS REQUESTED
The backend for frontend can be either a traditional web backend or an API. In the latter case CORS is used.
See this code example for an API driven approach. Any Authorization Server can be used as the token service. Following the tutorial may help you to see how the components fit together. SPA security is a difficult topic though.

Approah on creating clients/realms for separate service (frontend and backend)

I'm new to keycloak and would like to check what is the common design on the said architecture.
I have 1 backend(quarkus) 1 frontend (angular) and 1 flutter.
I would like to see if I could leverage the features of client. My idea is to have a separate client within the realm. For example
REALM = MyAppRealm
Client = backend-client and front-endclient
Is it possible that the token i got from front-endclient can be use to access the api from the backend?
Reason I have this setup is that the front-endclient has a public accesstype while the backend has confidential
to sum up. I would like to see if i can reuse the token i got from front-endclient to my backend-client
Yes of course you can do that.
The purpose of openid is to share authentication and authorization between a diversity of clients without needing to share credentials (no password is known by any of the clients). A trusted third party (here Keycloak) will give back a signed token in exchange for credentials. And this token will be a proof of who the user is and what he is allowed to do in the communications between the frontend and backend.
To sum up :
Your angular frontend authenticates an user using a public client and an implicit flow. When successfully authenticated, the frontend obtains an access token and a refresh token.
When making a REST call to the backend, your frontend needs to set the header Authorization using the access token as a bearer token ('Authorization: Bearer insert access token here'). You can automate this by using an interceptor (example)
Finally, when your backend receive an API request it can check the Authorization header to ensure the request is authenticated and authorized. For how to do that with Quarkus, everything is explained in this documentation page : https://quarkus.io/guides/security-openid-connect

How to communicate frontend with microservice architecture?

I'm struggling with setting up reliable and performant solution to communicate frontend with different microservices. I do not really now how to maintain (maybe not need) CSRF between my frontend and end services
Solutions stack: PHP, Laravel Passport, JWT, oAuth 2.0, Axios
Current approach:
Actually I've started up with approach from Laravel's passport
https://laravel.com/docs/5.4/passport#consuming-your-api-with-javascript
Using oAuth 2.0 to authorize user from website A to service B.
JWT token is returned for further communication.
Token is saved in cookie within website A
Once user is authorized website A uses JWT token to manage requests without additional to oAuth server, by sending JWT token as cookie using HTTP headers (withCredentials) to authorize user.
For each website A's request there was CSRF token created from service B since user is authorized and cookie could be applied by another unauthorized website to access service B. That was killing my performance since it has to retrieve CSRF for each request made. (that what I actually assume from laravel passport approach and need to create CSRF with JWT token - maybe that was mistake)
My concerns:
Regarding to of James Ward post:
http://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services
The easiest way to do authentication without risking CSRF
vulnerabilities is to simply avoid using cookies to identify the user.
Cookies themselves are not the cause of CSRF vulnerabilities. It’s
using the cookies on the server to validate a user that is the cause
of CSRF. Just putting an authentication token into a cookie doesn’t
mean it must be used as the mechanism to identify the user.
From my understanding setting JWT with website A's cookie with its domain set could not be accessed via any other site from outside. Since that there is no possible way to make request to service B without accessing JWT.
So do we really need CSRF then to secure potential attack to service B while using JWT?
If so, how could I achieve the best (in term of performant) way to generate CSRF through different services to be sure that communication would not be vulnerable for attack from different sites?
Any advice will be appreciated!

DC/OS - authentication vs. api token

As far as i know, DC/OS has two different types of tokens:
authentication token: retrieved via a login through
https://public-master-ip/login?redirect_uri=urn:ietf:wg:oauth:2.0:oob. This token is used to retrieve api tokens.
api token: retrieved via a post call to https://public-master-ip/acs/api/v1/auth/login with the authentication token in the request body. This token is used to authorize calls against the apis. Such a token expires after 5 days.
My questions are
Are my assumptions correct?
Does a authentication token expire? If so, when and is there a way to refresh it?
Let me first define the goal of the current (1.8) Open DC/OS authentication procedure and then walk through your assumptions. I'll answer your questions after that.
Goal
The goal of the current Open DC/OS authentication procedure is to use Auth0 infrastructure for triggering a single sign-on authentication flow against one of three popular identity providers, and have the result reported back to your DC/OS cluster. If the DC/OS cluster is happy with the result, it will emit an authentication token specifically adjusted to that individual cluster.
Comments on your assumptions
authentication token: retrieved via a login through https://public-master-ip/login?redirect_uri=urn:ietf:wg:oauth:2.0:oob. This token is used to retrieve api tokens.
That's roughly true. However, what you call "authentication token" actually is an OpenID Connect ID Token emitted by an OpenID Connect identity provider.
Let us take this one slowly, as it is a little involved.
What happens behind the scenes is an OpenID Connect single sign-on authentication flow.
More precisely, the DC/OS UI displays an iframe that loads a piece of JavaScript hosted by Auth0, which -- when executed in your browser -- performs the so-called implicit flow (which is one of three specified OpenID Connect authentication flow types).
By the end of this flow(*) the JavaScript code executed in your browser receives a so-called OpenID Connect ID Token (from the identity provider, of course, which is Auth0 in this case). This token is a JSON Web Token (JWT, see RFC7519) signed with the private key of the identity provider (in this case it actually is Auth0, which basically proxies other identity providers such as Google Accounts).
The piece of JavaScript that receives the ID token then -- as you say -- POSTs the ID Token to your DC/OS cluster (to https://public-master-ip/acs/api/v1/auth/login). The receiving end is a web application behind DC/OS' Admin Router (the latter is just a pimped nginx). That web application inspects the ID Token's payload (which is JSON) and finds the key/value pair "iss": "https://dcos.auth0.com/". So it knows who (pretends to) have issued that token! Then it goes ahead and fetches https://dcos.auth0.com/.well-known/openid-configuration (wooo, where does it know that URL from? This is magic defined by OpenID Connect Discovery 1.0 and RFC5785). That JSON document there defines a JSON Web Key Set (JWKS) document (specified via RFC7517), revealing the public key corresponding to the private key that had (supposedly) signed the ID Token. So, that web application goes ahead and fetches the public key directly from the identity provider (through HTTPS). It then uses that public key to verify the cryptographic signature of the ID Token (and it checks the expiration time, too, of course). If ID Token validation passes, the DC/OS web application I talked about rightfully assumes that the user agent that had sent the ID Token is successfully authenticated against Auth0. And, trusting Auth0, we rightfully assume that the user agent is authenticated against e.g. Google Accounts.
Only then it (the small web application in DC/OS I talked about) stores the identity within DC/OS, assigns a unique user ID, and emits the DC/OS authentication token. That token refers to the given identity via the named user ID.
(*)Note that the identity provider only emits the ID Token towards your browser after you have successfully authenticated yourself towards that provider (e.g. Google Accounts) and after you have given consent to share identity details with a third-party service.
api token: retrieved via a post call to https://public-master-ip/acs/api/v1/auth/login with the authentication token in the request body. This token is used to authorize calls against the apis. Such a token expires after 5 days.
In DC/OS terminology, this is the DC/OS authentication token. It is a JWT signed with a random key only known to your DC/OS cluster. The Admin Router in your DC/OS can validate such authentication tokens. Certain HTTP requests against Admin Router are only proxied to back-end services when they contain a valid authentication token in the request (hence, this token mainly serves authentication, but also a very basic coarse-grained authorization, if you want to say so). Otherwise, Admin Router will respond with a 401 (read: "not authenticated").
Answers to your questions
Are my assumptions correct?
I hope to have clarified
that what you call "authentication token" is an OpenID Connect ID Token (a JWT).
that what you call "api token" is what's called "DC/OS authentication token" in the DC/OS ecosystem (and it's technically a JWT, too).
Does a authentication token expire?
I read this question as "Does an OpenID Connect ID Token expire?" Yes, indeed! This is what the spec says about ID Token expiration:
exp -- REQUIRED -- Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this parameter requires that the current date/time MUST be before the expiration date/time listed in the value. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular.
Note that the spec does not enforce a particular ID Token lifetime -- this is up to identity providers to set. In case of ID Tokens emitted by dcos.auth0.com I have just confirmed that
>>> exp = 1474742063
>>> iat = 1474310063
>>> lifetime_days = (exp - iat) / (60.0 * 60 * 24)
>>> lifetime_days
5.0
That is, the ID Token emitted by Auth0 expires after 5 days.
If so, when and is there a way to refresh it?
You can only obtain a new ID Token emitted by Auth0 by going through an OpenID Connect authentication flow involving one of the configured identity providers. That is, the (only) intended way to obtain such a token and pass it along to DC/OS is triggered through the DC/OS UI which starts the Auth0-based flow for you (well, you could technically hack this together yourself...).
Note that Enterprise DC/OS offers an OpenID Connect authentication flow that
directly communicates the ID Token securely between DC/OS and the identity provider (no user agent ever sees that ID Token).
enforces the usage of the optional nonce mechanism of OpenID Connect ID Tokens (described in the spec), introducing more conceptual security on multiple levels (e.g. mitigating replay attacks).
We will probably merge that functionality into Open DC/OS by one of the next releases (no promises at this point!).
I hope that helped, let me know if there are further questions.

Facebook OAuth 2.0 "code" and "token"

Why do you need both a "code" and a "token" in the Facebook OAuth2 authentication flow as described here: https://developers.facebook.com/docs/authentication/ ?
If you look at the OAuth dialog reference (https://developers.facebook.com/docs/reference/dialogs/oauth/), it seems like you only ever use the token to fetch information about the user, and if you specify the response_type parameter as token or code,token, then you get the token on the first time.
Why do you need to get a "code" and then use the code to get a "token" as opposed to getting the token directly?
I guess I'm misunderstanding something basic about how OAuth works, but it seems you avoid the request to https://graph.facebook.com/oauth/access_token entirely if you get the token the first time with the dialog.
Let us take a simple example to differentiate authentication code vs access token.
You as a user want to try a new Facebook app called Highjack.
So you click on the application and the Highjack app asks you to log into your Facebook account. When you are done, Facebook generates an authentication code for you.
This code is then passed to the Highjack server which uses its own FB client id, FB secret and your authentication code to get an access token.
In the above example the authentication code is confirming you as a user is a valid FB user. But the second steps says "you as a FB user is giving access to the Highjack app for certain resources".
If the Highjack app wanted implicit grant (i.e direct access token), then the access token would be visible to you also since it is being exchanged with the browser. This means you can now call all Facebook APIs on behalf of Highjack using the access token. (You can only use the access token to get your personal information but Facebook has no way of knowing who is calling their APIs.)
Since we have 2 parties (You and Highjack) authenticating with Facebook we have this 2 fold mechanism.
Borrowed shamelessly from Salesforce Documentation:
Authorization Code
An authorization code is a short-lived token representing the user's access grant, created by the authorization server and passed to the client application via the browser. The client application sends the authorization code to the authorization server to obtain an access token and, optionally, a refresh token.
Access Token
The access token is used by the client to make authenticated requests on behalf of the end user. It has a longer lifetime than the authorization code, typically on the order of minutes or hours. When the access token expires, attempts to use it will fail, and a new access token must be obtained via a refresh token.
From the OAuth 2.0 Spec:
The authorization code provides a few important security benefits
such as the ability to authenticate the client, and the transmission
of the access token directly to the client without passing it through
the resource owner's user-agent, potentially exposing it to others,
including the resource owner.
So, basically - the main reason is to limit the # of actors getting the access token.
"token" response is intended primarily for clients that live in the browser (e.g.: JavaScript client).
Answer) You need/want both the code and token for extra security.
According to Nate Barbettini we want the extra step of exchanging the authentication code for the access token, because the authentication code can be used in the front channel (less secure), and the access token can be used in the back channel (more secure).
Thus, the security benefit is that the access token isn't exposed to the browser, and thus cannot be intercepted/grabbed from a browser. We trust the web server more, which communicates via back channels. The access token, which is secret, can then remain on the web server, and not be exposed to the browser (i.e. front channels).
For more information, watch this fantastic video:
OAuth 2.0 and OpenID Connect (in plain English)
https://youtu.be/996OiexHze0?t=26m30s (Start 26 mins)
If you look at the flow of Authorization Code OAuth type, yes, there are actuary two steps:
<user_session_id, client_id> => authorization_code
<client_id, redirect_uri, authorization_code, client_secret> => access_token, refresh_token
In step1: the user tells the OAuth Server that "I want to auth this client (client_id) to access my resource. Here is my authentication (user_session_id or what else)"
In step2: the client (client_id) tells the OAuth server that "I've got the user the authorization (authorization_code), please give me an access token for later access. And this is my authentication (client_id & client_secret)"
You see, if we omit step 2, then there is no guarantee for client authentication. Any client can invoke step1 with a different client_id and get an access token for that client_id instead of its own. That's why we need step2.
If you really want to combine step1 and step2, you can do something like this:
<client_id, redirect_uri, client_secret> => access_token, refresh_token
We use this approach in our Open API Platform, and we haven't find any security problem yet.
BTW, there is actually an Implicit Grant type, that is:
<client_id, redirect_uri> => access_token, refresh_token
It is generally applicable to client only application which have no server backend. In that case, the OAuth server must ensure that the redirect URI belongs to that client (same with the register redirect_uri, for example).
The mix-up came because the user on behalf of himself and not the client app authenticate against the authorization server (i.e. facebook).
Its much simple to secure the client app (with https) then the user-agent (browser).
Here is the original formulation from IETF-oauth (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-threatmodel-08#section-3.4):
3.4. Authorization Code
An authorization code represents the intermediate result of a
successful end-user authorization process and is used by the client
to obtain access and refresh token. Authorization codes are sent to
the client's redirection URI instead of tokens for two purposes.
Browser-based flows expose protocol parameters to potential
attackers via URI query parameters (HTTP referrer), the browser
cache, or log file entries and could be replayed. In order to
reduce this threat, short-lived authorization codes are passed
instead of tokens and exchanged for tokens over a more secure
direct connection between client and authorization server.
It is much simpler to authenticate clients during the direct
request between client and authorization server than in the
context of the indirect authorization request. The latter would
require digital signatures.
Theoretically,
Access Tokens cannot tell us if the user has authenticated but auth code does.
Auth code should not be used to gain access to an API but access token should be.
If you have a single page application or mobile application with no or minimum backend, your application may want to access user's FB data directly at frontend. Hence the access token is provided.
In another case, you may want a user to register/login to your app using some external auth service provider like Facebook, Google etc. In this case, your frontend will send the auth code to the backend that can be used to get access token from Facebook at serverside. Now your server becomes enabled to access user's FB data from the server.
Basically, as an extension of Lix's answer, the access code route allows a Resource Owner (i.e. the Facebook User) to revoke authorization for their User Agent (i.e. their browser), e.g. by logging off, without revoking authorization for an offline Client (i.e. Your Application).
If this is not important, then there is no need to use the access code route.
Furthermore, the access code is provided to ensure that the Token provided to a server is actually registered to the Resource Owner (i.e. the Facebook User), and not the User Agent (or a Man-in-the-Middle).
This seems similar to the question of either choosing the implicit vs authorization code grant flow. In fact, here is what looks like an opposite view point?!.
Also, as Drew mentioned,
When the access token expires, attempts to use it will fail, and a new access token must be obtained via a refresh token.
another piece is the refresh token, but I don't see that being explained too well in the FB Docs. If I'm correct, the implicit grant (the direct token) should be really short lived, but that is to-be-enforced and FB.js seems to hide a lot of that (this one I have not looked as deep into).
If I'm correct, the code%20token is an optimization allowing both the User Agent to have a token and allowing for the server to initiate the token exchange process in a single request (as anything over Network IO is considered expensive, especially to a User Agent).
In OAuth 2.0 with facebook, the overall concept is simple as follows.
Step 1. Obtain "Authorization Code" by a GET request
request URI: https://www.facebook.com/dialog/oauth
Params:
response_type=code
client_id={add your "App id" got by registering app}
redirect_uri={add redirect uri defined at the registration of app}
scope={add the scope needed in your app}
Headers: None
Step 2. Obtain the "Access Token" by sending the authorization code as a POST request
URI: https://graph.facebook.com/oauth/access_token
Params:
grant_type=authorization_code
client_id=<add your "App id" got by registering app>
redirect_uri=<add redirect uri defined at the registration of app>
code=<obtained authorization code from previous step>
Headers:
Authorization:Basic encode <App Id:App Secret> with base64
Content-Type:application/json
Step 3. Use the access token got from above step and retrieve user resources
It’s because the access token is given to an AUTHENTICATED client (third-party app) using a shared secret that only FB and the client knows. The only way that the user could directly request the access token is by knowing the shared secret, which would make the secret public and could lead to a man-in-the-middle attack. Further, while FB can guarantee a secure connection to the user, FB can’t guarantee the handoff of the token to the client is secure. However, FB (and OAuth2) does require a secure connection between the client and FB. The access token is tied to the client public ID (usually hashed), which means only the original client application can use it to request the token because the secret is sent along with the authorization code to get the access token.
You recieve a token when the user logs in. But you might want to change the token when you are performing other actions. EG posting as your app/page or posting as a user with offline_access.