Is client secret ever a secure method to refresh access token for an SPA in authorization code flow? - security

In the article at MSDN, it says that the following request needs to be performed to get a new access token using a refresh token for a web app.
POST /.../v2.0/token
Content-Type: application/x-www-form-urlencoded
?client_id=...
&scope=wide_and_proud
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFP...
&grant_type=refresh_token
&client_secret=hakuna_matata
It means that we'd need to distribute the client's secret to the frontend application, which, to me, opens a huge security gap. I want to argue that we should never-ever feed our SPA with the information on client's secret.
I've been googling it for a few days and all the resources somehow point to a case where we do provide client's secret. However, I can't shake of the sensation that it's not what's supposed to be applied in the case of an SPA authenticating using authorization code flow. However, I see no documentation specifically discussing the combo: "spa refresh token authorization code flow".

You are right that the SPA should not use a client secret. It should also avoid use of refresh tokens. The problem is that the SPA has nowhere secure to store this information.
PKCE
An SPA is a public client and can use a one time use runtime secret rather than the client secret field. See this Proof Key for Code Exchange summary for details. This would solve the initial problem.
The Token Handler Pattern
If you want to go further, there is a wider issue is that an SPA needs a server component (API) that will look after its secrets and tokens. The SPA can then make a request such as this and the API can deal with sensitive data:
POST /login/end { url: location.href }
If this is done in an API driven manner it also fits very nicely with the goals of an SPA architecture, though it does add more moving parts. See these resources for further info:
React Code Example
Simplified SPA OAuth Code
Docs
Note also that this is a general design pattern. It will work with Microsoft and any other Authorization Server.
Token Handler First Steps
You could start with just these steps, which would also keep refresh tokens out of the SPA:
SPA calls API at /login/start to get the redirect URI
API sets a temp cookie with state + PKCE parameters
SPA redirects and receives the response
SPA calls API again, at /login/end
API then supplies the client secret to the Authorization Server
API stores refresh tokens in a secure cookie
API returns short lived access tokens to the SPA

Related

OAuth clarification

I've followed a training in Go as an introduction to microservices architecture a while ago. Getting back to this project I realise that I need more context as we've been quickly digging into the details of the implementations at the time...
I've drawn a simplified sequence diagram of 2 simple use cases:
The user logs in
The user is already logged in and make a purchase
(you can comment / modify the diagram at your convenience)
https://drive.google.com/file/d/1gWgkhJipUvWrVidkl7YFt_xlDmZYn_CX/view?usp=sharing
Here are the questions I have:
Here we're dealing with user authentication but what about client authentication? In the case of a web front end client, can I imagine storing an api_key and an api_secret in the env variables for the server that will be hosting this client? Because there use cases where the user is not logged but some services still needs to be available, but at the same time I only want my known clients (the web front and the mobile app) to be able to access those services (putting aside API Gateway solutions, and maybe other API Shields which would probably add another security layer against DOS etc.)
If the client logs in using Google/Facebook, the front app will receive an id_token that needs to be passed to the backend which would then verify the token ( https://developers.google.com/identity/sign-in/web/backend-auth ). In this particular case my OAuth API would not be used. Could please you confirm that it's the way it should be handled?
Many thanks.
EDIT 02/05/2022
Intro / Context
First thing first, Authorization is not Authentication.
Authentication is the process of verifying who a user is,
while authorization is the process of verifying what they have access to.
And like #Max said, OAuth is designed to manage Authorization and Open ID Connect (OIDC) is an extension of OAuth to manage Authentication on top of it.
The diagram I've exposed in my question is known in the OAuth world as the password grant, and, from the official documentation :
Because the client application has to collect the user's password and
send it to the authorization server, it is not recommended that this
grant be used at all anymore.
Authorization of my App (to get access to my APIs)
From the API perspective, we just want to ensure that the incoming requests are coming from the server that is hosting the App. So, in my case, it's simple machine-2-machine communication from backend server to backend server and there's no action required from the user. So, I must implement the Client Credentials Flow
...which would lead me to this flow:
https://drive.google.com/file/d/1qE9JpWRSRPa8z5iNxm7ocGkeT0E149Sv/view?usp=sharing (Feel free to comment / rectify )
Authentication of a user
Because OAuth knows nothing about authentication, I need an OIDC flow. The easiest one is based on the Authorization Code Flow with PKCE from OAuth below (only about authorization) ...
... but the difference is that we're passing an additional scope named openid in the authentication request (step 3), when the app performs the 2nd request to the token endpoint (step 7), the auth server returns an ID Token (which is a JWT containing user info in the payload -> authentication) in addition to the access_token (which do not contain user info but just "random string"). There's other OIDC flows with their pros & cons depending on the situation but it's another topic on its own (https://openid.net/specs/openid-connect-core-1_0.html)
User already identified by Google/Facebook
In case the client logs in using Google, the front app will receive an id_token. This token could be sent to the app server which would then make a request to the API Gateway, which then call the Auth api which would be in charge of verifying the token by calling the 3rd party auth server ( https://developers.google.com/identity/sign-in/web/backend-auth ).
In case of Facebook, we get back an access token, so I don't know how to deal with it ...
https://developers.facebook.com/docs/facebook-login/web
https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
Using Firebase, there's an onAuthStateChanged callback, so from the App perspective it will prevent request without the user being logged in, but from the API perspective, it doesn't guaranty that a request is coming from a logged in user...
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
Warning: the answer below is not complete, it only serves to give a rough idea
Intro
OAuth2 is a protocol for authorization.
Grant Types
Over the OAuth2 protocol, you can use one of the "grant types" or "flow", one of these flows is illustrated in the picture you post and named password grant.
Each of these flows is realized for different scenarios, you rightly point the attention to how securely store the password on a web app.
For example for a front-end authentication (javascript / wasm) you can use a PKCE Flow where the secret_id is not used.
Endpoints
On OAuth2 there are two primary enpoints
Authorize endpoint - where you obtain the authorization code
Token endpoint - where you exchange the authorization code for the token(s)
Tokens
There are two types of tokens on OAuth2
Access Token
Refresh Token
The definition of token on OAuth2 is "an opaque string", you don't need to read it.
The access token is used against the API, this token has an expiration date, when it is expired the system use the refresh token to get another access_token whitout user interaction, when the refresh_token expire the user must re-authenticate again.
You can read the content of the access_token (which is a JWT token) from the JWT.io
Scopes
The Access token has, on its body, the scopes (i.e. Read email, read name, etc).
Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account.
Identity
On top of the OAuth2 are build other protocols OIDC aka IdToken aka Open Id Connect is one of them, in other terms OIDC protocol use the OAuth2 for establish an Authentication.
With the OIDC there is another token the id_token, this token came with the user's info and is NOT used has Authorizization in front the API.
There are also OIDC flows you can use to get the id_token and/or the access_token.
Conclusion
I suggest you read about OAuth2 from the references below and try different flows using the playground
References
Which oauth2 flow should I use
OAuth2
PKCE in more depth
My advice is to start with the data, which is the deeper area of OAuth.
USE AN AUTHORIZATION SERVER
This will enable you to keep your code simple. It will also handle Google / Facebook and many other forms of authentication for you, with zero impact on your code. The Curity Community Edition is a free and developer friendly option, though there are others. eg Keycloak, Ory Hydra.
PROTECT DATA
OAuth primarily gives you modern ways to protect data. Use scopes and claims to protect data across multiple microservices in a zero trust manner while securely maintaining user context. You will also need to manage joining identity and business data.
IMPLEMENT UI FLOWS CORRECTLY
Mobile apps use the AppAuth pattern. The current best practice for browser based apps is a Backend for Frontend approach. Both of these are tricky.
KEEP CODE STANDARDS BASED
All of the above Curity resources are based on OAuth related standards. If followed your apps will stay simple, with portable code, that can also work with other providers.
OAuth is very architectural though, and the best designs take time to learn, but you can learn the intricacies gradually. Our IAM Primer is a good starting point.

Should I use an access token as API-KEY?

BACKGROUND
I have an application with a node backend and an angular frontend. The backend has two GraphQl endpoints:
/auth, which has methods like:
signIn, which authenticate an interactive user (basic usr/pwd) from my angular front and and returns an access token and a secure httpOnly refresh token (cookie);
refreshToken, which returns a new access token; and
signOut, which just revokes the refresh token.
/api, which contains the business rules, and authenticates the request by the access token received (stateless)
The angular frontend authenticates the user by calling the /auth/signIn endpoint, keeps the access token in memory, is unaware about the httpOnly refresh token, and creates a timer to call the /auth/refreshToken periodically.
PROBLEM
I need to grant access to my customers to access the /api programmatically too (e.g. from Zapier), so we are talking about an API-KEY, right? I was thinking about creating an API-KEY section in the SETTINGS area in the frontend and CRUDE methods in the /auth endpoint to maintain them. I would create a new special non interactive “user”, in the users table linked to the generated API-KEY so that, for instance, the user Zapier would be related to the API-KEY created to interact with Zapier and I could see its activity along the other users activities at the audit trail and easily revoke it by deleting that user.
QUESTION
Should I use a long term (?) access token as API-KEY? Wouldn't that defeat the purpose of using access tokens? My /api would no longer be stateless and I would have to check the existence of the access token for each request, right? It doesn’t seem the right choice. Is there a better approach?
Using the refresh token as API-KEY doesn’t seem to be an option to me, first, because it doesn't seem to be allowed to set a httpOnly cookie on the client side, second, because the logic to update the access token would be too complex to the user and third, because I wouldn't want to expose the /auth endpoint.
API Keys are very weak security so ultimately this depends on data sensitivity such as whether it is serious if an attacker can access your data easily for a long time.
I would favour a standard OAuth flow for the type of client - perhaps Client Credentials Grant - so that access tokens are used, which are valid for a limited time period, such as 30 minutes. Expiry and refresh coding is well documented online.
If it is a user app then maybe the app (or its back end) needs to do some work to get tokens correctly.
If the customer is writing code to call the API with code then they need to be given guidance on how to authenticate and manage expiry. These details are well documented online.
Attaching a token could even be managed by an outbound proxy if these users are very non technical, where the proxy deals with supplying a token.

Authentication with JWT in React and Node-Express using Github OAuth

I have been trying to implement Google and Github OAuth authentication in one of my projects which uses React on the client side and NodeJS-Express on the backend. After going through dozens of articles and several Youtube videos later, here's what I have discovered about OAuth.
There are two main flows in OAuth. Authorization Code Grant Flow and Implicit Flow. To be on the same page, I would elaborate what I understood about both of these flows in short:
Authorization Code Grant Flow : User(resource owner) clicks on Login with Google/Github and is then redirected to the consent screen. Once they give consent, user(resource owner) is redirected back to the callback_url with a authorization_code in the URL query parameter. The authorization code is then send to the backend server. The server then makes a request to the authorization server of the OAuth provider along with the client_id,client_secret and the authorization code and then receives a access_token as well as a refresh_token required to access the resource server.
Implicit Flow : It is sort of a hacky way that was proposed back in the day for Single Page Applications as CORS wasn't properly implemented in all the browsers. Here the Single Page Application is given the access_token once the resource owner has given consent to the OAuth provider and it is now the duty of the SPA to store the access_token in a protected manner. However, since browsers aren't really trustworthy, and CORS is a real thing now, the Implicit flow is not really recommended anymore.If someone wants to implement the Implicit FLow, PKCE (Proof Key for Code Exchange) is sort of the standard now.
What I tried to implement
I decided to go ahead with Authorization Code Grant Flow as it seemed the most secure to me and also I have a backend server to handle the authorization code exchange.
Here is what most people suggest in order to implement Authorization Code Grant Flow with React.
Render a link like this.
<a href='http://localhost:8000/auth'>Login With Github</a>
Now handle this endpoint in the backend
app.get('/auth',(req,res)=>{
res.redirect(githubOAuthConsentScreenURL)
})
Now handle the redirect_uri in the backend and make a post request to the authorization server to get a access_token
app.get('/auth/callback',(req,res)=>{
//Extract the authorization code from query params and make a POST request to get back the access_token
})
Passport JS also implements this approach which is handling the callback url on the server.
Here is what I thought of doing:
Handle the callback URL on client side i.e. with React. Extract the authorization code from the parameters and then make a XHR call to the server with it. The server will now exchange the authorization code for an access_token and make a request to get the user's profile data. If everything succeeds, the Express backend will return a short lived access_token and a long lived refresh_token which will then be used by the React application to access the REST API.
My question here is : Which method is correct and the standard way to do authentication, handling the callback_url on the server or on the client side?. The method I propose over here seems more logical to me. Does my method have any security flaws compared to the other one? Am I missing something?
Other things that I have confusions about :
How is OAuth vulnerable to CSRF? From what I read, the state parameter can be used to protect the OAuth flow against CSRF. However, if I am redirecting the user from my backend server and then handling the callback_url in the server as well, how do I remember the state variable apart from storing it in some sort of session/db. Is it not more logical to redirect the user from the browser? Then the state parameter can be stored in the localStorage and can be matched later during the callback.
Also, I am implementing a short lived access_token and a long lived refresh_token for authentication and storing both the tokens as httpOnly cookie. If my access_token is stored as a httpOnly cookie, then how do I know if I am logged in or not and persist the state in React. One solution (proposed by Ben Awad in this video) was to query for the user during initial load and if the query succeeds, store the state (maybe in Redux) and then conditionally render the routes.
Is this the correct way of doing this? If not what is the standard manner that is followed by React applications which are actually in production? Again, am I missing something here?
Please Note : I am pretty new to authentication, would appreciate all the help and detailed explanations.

OAuth2: How to pass session data to the front end?

In my OAuth flow, I am using the auth code grant type.
The front end (React.js) app directs to the OAuth server's login and scope grant pages
A redirect happens to an Express.js client app
The client app receives the auth code and does token exchange.
I am stuck at this point. i have saved the user and token data to a database. But I have no way of redirecting back to the front end (React.js) app while safely passing a user session:
Cookies can't be passed cross domain
Query strings are available but are captured in server logs and browser history.
Redirects are GET requests so I don't have access to a POST request body.
How do you safely pass session data to a front end after your OAuth process? I suspect my Auth flow is wrong at one or more points.
Sounds like you could simplify by using a client side flow in your ReactJS app:
Login uses Authorization Code Flow (PKCE)
SPA uses the OIDC client library
The SPA receives an access token and can make cross domain API calls with it
It is difficult to see what value your ExpressJS client app brings - feels like it is adding unnecessary complexity.
RESOURCES OF MINE
Here are some notes that might be useful. The SPA code is quite a bit simpler than older solutions that had to switch between front and back ends' to handle security processing:
SPA and API code sample
Blog post on OAuth / HTTP messages

OAuth2 process and best practices for private apps

Please bear with me while I explain my problem and the solutions/guides I have found.
Description: In my company, we have one product that have multiple modules. Each module is its separate backend and frontend. We have JavaEE/JakartaEE with JAX-RS as our backend stack and React as for our frontend. Until now we are using Basic Authentication using the JavaEE Security via Sessions, but because the product is evolving and we need mobile clients and allow third parties to access the data, we have decided to integrate OAuth2/OpenID Connect into our application.
Since there are multiple implementations out there that provide OAuth2 functionality, we are currently looking into a few available options. (Keycloak and ORY Hydra for example). The decision which we will choose depends on how much work we want to do change the existing structure of the application how we handle the users in the database. But regardless of which implementation we opt for, we have similar questions going forward.
Questions
How do the react applications handle login process and token storage?
Every documentation says: If the user is not logged in s/he is redirected to the login page. Where after login and consent he is redirected back to the app (After completing the oauth2 workflow obviously) with the Access/ID Token for the resource server and/or Refresh Token for refreshing the Access/ID Token.
Now here is what is not clear to me:
Since this is our own React app, we do not want to show the consent screen, like in apps from Microsoft/Google etc you do not see any. I guess this is possible by setting a value in the request itself, or skipping the consent screen based on the client id but I just want to make sure.
Next is where do I store the Access and Refresh Token? Access Token should be sent as the Bearer token with each request. So it can be stored in local storage because they are short lived, but the refresh token should be stored securely. Like in a secure http cookie?. If that is the case, then the server has to set it. If this is correct is this how the flow will look like?
Our React App (Not logged In) --> Login Page (Another React Page) --> User Enters Credentials --> Java Backend --> Authenticates the user --> Initiate the OAuth2 process --> Get the Access and Refresh Tokens --> Set them as secure Cookies --> Return the authenticated response to frontend with the cookies --> Login Page redirects to the previous page --> User continues with the app
This does not feel correct. How would PKCE help in this case?
Assuming what I wrote above is correct, I would need different login flows when the users logs in from our own app or from a third party app. That can however be determined by checking client ids or disabling password flow for third party clients.
The same would be applicable then for the refresh token flow too. Because for my own app I have to set the cookies, for third parties this has to be directly from the OAuth Server
Resources I have read/researched:
https://gist.github.com/mziwisky/10079157
How does OAuth work?
Edit: Adding more links I have read
What is the purpose of implicit grant
Best practices for session management
RESTful Authentication
And of course various writings and examples from Keycloak and ORY Hydra also.
I am currently trying both Keycloak and ORY Hydra figuring out which fits our needs better.
Thank you all in advance!
You don't have to show the consent screen. Here's an example of a React app authenticating using the Authorization Code Grant: https://fusionauth.io/blog/2020/03/10/securely-implement-oauth-in-react (full disclosure, this is on my employer's site but will work with any OAuth2 compliant identity server).
The short answer is that it's best for you to avoid the implicit grant, and have the access and refresh tokens stored in some middleware, not the browser. The example in the link uses a 100 line express server and stores those tokens in the session.
I wrote a bit about PKCE. Excerpt:
The Proof Key for Code Exchange (PKCE) RFC was published in 2015 and extends the Authorization Code grant to protect from an attack if part of the authorization flow happens over a non TLS connection. For example, between components of a native application. This attack could also happen if TLS has a vulnerability or if router firmware has been compromised and is spoofing DNS or downgrading from TLS to HTTP. PKCE requires an additional one-time code to be sent to the OAuth server. This is used to validate the request has not been intercepted or modified.
Here's a breakdown of the various OAuth options you have (again, this is on my employer's site but will work with any OAuth2 compliant identity server): https://fusionauth.io/learn/expert-advice/authentication/login-authentication-workflows You can allow different flows for different clients. For example, you could use the Authorization Code Grant for third parties and the Resource Owner Password Credentials grant (which essentially is username and password) for your own applications.
I'm not sure I answered all of your questions, but I hope that some of this is helpful.
The OAuth 2.0 Security Best Current Practice should be consulted. Even though it is still a "Internet Draft" it is mature and has been implemented by several vender implementations.
In general the OAuth 2.0 Authorization Code with PKCE Flow is the recommendation regardless of the use of Bearer tokens or JWT.
You should also consider reading about WebAuthn (Where there is not password)

Resources