Microsoft Graph - OAuth2.0 flow for React client and spring-boot backend - reactjs

I am building a React-based SPA that communicates with a spring-boot backend via a REST API. I need the user to be able to log into their Microsoft account on the browser client (the SPA) and I need the backend service (spring-boot app) to be able to query Microsoft's Graph API on behalf of that user.
After reading up on the Oauth2 flows, the authorization code flow (not the PKCE flow, just the regular authorization code flow) seems the most appropriate. The browser client could let the user log into their Microsoft account, retrieve an authorization code, and send the authorization code to our backend service via HTTP request. The backend service (which is trusted and can safely store a client secret) can then request an access token, make requests to the Graph API directly (meaning that the SPA would never need to make any requests to the Graph API), and silently refresh the token as needed.
However, I cannot see any examples of anyone using this flow to access Microsoft's Graph API.
Looking at Microsoft's documentation, it seems like they recommend using the on-behalf-of flow. But this flow requires the browser client to request an access token and then use that to communicate with the backend service (which in turn can communicate with the Graph API). It doesn't make sense to me why the access token cannot be requested on the backend using a client secret. Wouldn't this be a more secure and preferred method than having the client retrieve the access token, as is done in the on-behalf-of flow?
The Oauth2.0 site, recommends that SPAs should either use the authorization code with PKE or the implicit flow, but I do not see an option to use the standard authentication code flow for SPAs. Should I take this as an indication that SPAs should not be using the standard authorization code flow as I described earlier?
Despite not finding a clear-cut example of the standard authorization code flow in Microsoft's documentation for a react frontend + java backend, I tried to go about doing this myself. However, using the #microsoft/mgt-react and #microsoft/mgt-element libraries to do this are not straight forward. For example, the #microsoft/mgt-element notion of a Provider supports a call to retrieve an access token, but doesn't clearly expose the authorization code. If I wanted to do the authorization code flow described earlier, it seems like I would need to use raw HTTP requests, which I know is not a recommended way of accomplishing this.
Summarizing my questions:
What OAuth2.0 flow should I be using: 1) authorization code (access token is retrieved by backend service using client secret), 2)
authorization code with PKE (access token is retrieved by client), or
3) on-behalf-of flow (access token is retrieved by client, seems to be an extension of PKE flow)?
If using the on-behalf-of flow, does the SPA just include the access token in the header (marked as 'bearer') and the backend service just
includes that same header to query the Graph API, or does the backend
service need to request another token before querying the Graph API?

Agree with #ch4mp to call graph api directly in SPA if it's allowed. If not, then I recommend you using on-behalf-flow or client credential flow based on your requirement.
Let's come back to your requirement -- call ms graph api in a springboot api project. First, let's see one of the graph api getting user api. You can see permission types here: Delegated which means call graph api on behalf of the user, Application which means calling api on behalf of the application(your spingboot api project) itself. If you want to call api behalf of the user, then you have to use on-behalf-of flow. This is because the api project which will be considered as a daemon application, so the project itself doesn't have a UI page to let users enter username/password to sign in and get authenticated.
You can certainly use ROPC flow which have to pass the username/password to api but I really think it unsafe, so I don't recommend.
If it's not necessary for you to call graph api on behalf of user, you can certainly take client credential flow into consideration. But pls note here, application type api permission is a "large" api permission which always have name like User.ReadWrite.All, Mail.ReadWrite.All and it always means the application can not only query user information but also be able to modify user information.
If you want to use on-behalf-flow, then you may review this answer and it explained the whole progress...

I would use authorization-code flow (with PKCE) to get an access-token and then refresh-token flow to "maintain" this token, both from client.
Authorizing the request to your resource-server with this token only makes sense if Microsoft authorization-server is your main authorization-server. Otherwise (user also logged in with an authorization-server of your own or not using OAuth2 betwean React and backend), you can still send Microsoft access-token in request body.
In any case, when issuing requests in the name of the user from the backend, do as you suggest: just set the access-token sent by the client as Bearer Authorization header (token is retrieved either from Spring security context or request body). Backend fetches a new access-token (using client-credentials flow) when issuing requests in its own name (without the context of a user).
Side note: have you considered calling Microsoft API directly from React client? If you don't have to store the result of that call on your resource-server (i.e. call graph API to display data and store only what user selected from that data), that would save quite some latency on the client and costs (network and CPU) on the backend.

Related

PCKE implementation for React SPA and REST API

I need to implement a system where the frontend is a pure SPA made with react (by pure I mean it is only hosted as a bunch of static files) and a nodejs REST API made with express. I can not use any 3rd party authentication providers like Auth0, etc. so everything must work locally.
The app must have authentication and it must be more or less up to modern standards. I am looking to implement PKCE flow with short-lived JWT auth tokens and long-lived refresh tokens.
There is one thing I am confused about. Every guide on PKCE flow always mentions redirects from one URL to another during the auth flow. In my setup, however, redirects are highly unwelcome.
I want to check and find out whether it is OK to implement PKCE flow using pure XHR requests to communicate between the SPA and API. Is it appropriate to do so? What major risks are there compared to a setup with redirects?
EDIT: to make this question more specific, what should be the PKCE authentication flow if you can only use SPA and XHR (and must avoid redirects)?
There is one thing I am confused about. Every guide on PKCE flow always mentions redirects from one URL to another during the auth flow. In my setup, however, redirects are highly unwelcome.
The whole point with OAuth 2.0 is the delegation (redirect) to the ID Provider, such that the user always send his password to the ID Provider instead of sending the password to each application. So you will always need to redirect the user to sign in - but hopefully the user is already signed in, so he immediately becomes redirected back to your app. There are techniques to check if the user is signed-in using XHR, that can be used until he need to sign in.
what should be the PKCE authentication flow if you can only use SPA and XHR (and must avoid redirects)?
In the Authorization Code flow, the client typically does two request to the ID provider. The request to authorization endpoint typically is done through a redirect (e.g. in case the user need to authenticate), but the second request, to token endpoint can typically be done in the background using XHR or fetch using appropriate CORS configurations.

Right way to use federated login (Google, FB etc) between a client & server

I have seen this solved in popular platforms like Android & iOS using client SDKs. My question is
I have a RESTful server
I have a mobile client
How to
Create a signup which uses a federated OAuth (Google, FB, Microsoft etc) and use that to further authenticate the subsequent API calls.
This is what I am thinking
Client application calls the OAuth dialog of the login provider, and receives (after user consent), access token and user ID.
This is stored on the client and also passed to the server.
Server can validate (retreive) user info using the accessToken.
Sever can return IDtoken/Refreshtokens which client can use in subsequent API calls.
My question here is
Is this the right approach.
Can clients store the accesstoken (best practice?)
Can client pass the accessToken to backend (best practice?)
Is there an example, how this can be implemented for Google Auth (for a client and Webserver) without using SDKs.
The standard option is to implement the AppAuth pattern in your mobile app, meaning it signs in and uses tokens from your Authorization Server (AS). The mobile app then sends access tokens to your APIs.
Once this is done, signing in via Google, Facebook etc requires only config changes in the AS. Adding a new login / identity provider requires no code changes in either your UIs or APIs.
Here are Android and iOS samples of mine that use this approach and which you can run and maybe borrow some ideas from.
They use AWS Cognito as the AS - and I could configure Cognito to use Google or Facebook logins if I wanted to. Also I am in full control of scopes and claims added to access tokens - which my APIs can use to authorize requests.
ANSWERS
Almost right - your client app redirects to the AS and not Google / Facebook directly
Yes - mobile clients can store a refresh token in OS secure storage private to the app so that users do not need to login on every app restart. My samples do that.
Yes - mobile clients use access tokens as API message credentials
LIBRARIES
I agree with you here - avoid Google / Facebook libraries in your app. However it is recommended to use the respected AppAuth libraries - once integrated your app is compliant with any AS and will support all of its authentication flows.
Out of interest the AppAuth pattern even potentially enables future advanced scenarios such as App2App, since the AS can federate to an AS from another company (though I doubt that is relevant to you right now).
LEARNING CURVE
Finally it's worth mentioning that it is tricky to implement AppAuth - there are annoyances - but once done the architecture is in a good place.
Firstly, Any application that calls Google APIs needs to enable those APIs in the API Console.
Now, Any application that uses OAuth 2.0 to access Google APIs must have authorization credentials that identify the application to Google's OAuth 2.0 server.
For the credentials go to the Credentials Page and then fill the form according to your Application.
Note: Google recommends that you design your app's auth endpoints so that your application does not expose authorization codes to other resources on the page.
After getting your credentials, download the client_secret.json file from the API Console and securely store the file in a location that only your application can access.
For HTTP/REST, there is no need to install any libraries to call oAuth 2.0
Google's OAuth 2.0 endpoint is at https://accounts.google.com/o/oauth2/v2/auth. This endpoint is accessible only over HTTPS. Plain HTTP connections are refused.
As a client, the only thing you need to do for Basic authentication is to include an Authorization header in an HTTP request, composed of the username and password, separated by a colon and then Base64 encoded. E.g., in Ruby (1.9) using RestClient:
require 'restclient'
require 'base64'
auth = "Basic " + Base64::strict_encode64("#{username}:#{password}")
response = RestClient.get("https://myhost/resource",:authorization=>auth)
The token value is opaque to a client, but can be decoded by a Resource Server so it can check that the Client and User have permission to access the requested resource.
Authorization: Bearer <TOKEN_VALUE>
Send user to Google's OAuth 2.0 server. Example URL:
https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly&
access_type=offline&
include_granted_scopes=true&
state=state_parameter_passthrough_value&
redirect_uri=http%3A%2F%2Foauth2.example.com%2Fcallback&
response_type=code&
client_id=client_id
Request access token. Example:
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code
Use API. Example:
GET /drive/v2/files HTTP/1.1
Authorization: Bearer <access_token>
Host: www.googleapis.com/
Is this the right approach.
Yes, pretty much. You're describing standard bearer token authorization. Access & ID tokens expire after a short time frame, and you have to use a long-lived refresh token to get new ones. The access token allows you to gain access, the refresh token is only useful for bootstrapping tokens.
Can clients store the accesstoken (best practice?)
Yes. You must store an OAuth refresh token. You may store the ID and access tokens (as opposed to keeping them in memory.)
On the web, you should not use Local Storage. Instead, use an httpOnly cookie.
On mobile, use platform features like Android's AccountManager or iOS' Keychain Services.
Can client pass the accessToken to backend (best practice?)
Yes, that's its purpose.
Is there an example, how this can be implemented for Google Auth (for a client and Webserver) without using SDKs.
I do not recommend this for several reasons. Firstly, your implementation will be less secure than Google's, who has a team devoted to ensuring their SDK is the most secure it can be. Secondly, you're not ready to try this implementation, yet. Start with the SDK, get it working, and then come back to this.
Using a solution like Auth0 will be the easiest way to start.

Google App Engine service-to-service authentication (non-programmatic)

I have a scenario where I have an on-premise GUI application which has a "Web Service Invoke" component which I use to call a GAE API.
In the GUI application, it is possible to:
store credentials
create a chain of web services/http(s) calls that can integrate an output of preceding https(s) call as a query parameter or header value into the next http(s) call
retrieve a value from JSON response of the http(s) call
But it is not possible to programmatically do anything. The Web Service widget has fields for passing API URL, query parameters and headers only.
I'm looking to do a service-to-service authentication for the API call and the API should be protected using IAP as well.
I'm aware of the recommended approach is to create a service account and provide the service account JSON key file to the client and the client signs a JWT token and pass it as a bearer token. But as I only have widgets, no programming possible, therefore signing JWT token is not an option.
I was looking at Google OAuth or IAM APIs which can support "client_credentials" grant type wherein I can get an access token using just clientId and clientSecret - something possible in Apigee. But it seems this grant type is not supported by Google OAuth APIs.
I also looked at Cloud Endpoints using API KEY - so the current thought is to the client passes API KEY in header/parameter for authentication to Cloud Endpoints, then Cloud Endpoints uses its service account to get access through IAP, and finally, I'm thinking that the client can also send in "Basic Auth" credentials through Authorization header which the GAE API backend service validates. The additional "Basic Auth" is because Google recommends using another auth method in addition to API KEY.
I would like a simpler solution if possible using GAE and IAP only. Any other suggestions, especially using time-limited tokens, that can work over only http(s) calls (non-programmatic or client library), is much appreciated.
Any alternatives or things to try is also appreciated.
Look into this server-to-server authentication using JWT Google API Authentication

Using IdentityServer to call API on behalf of a Client not User?

I am thinking of using IdentityServer4 for a new project. I am new to this and have done some reading and seen some of the demo clients in action.
Most of the clients in the samples allow login as a User and issue a token.
Requirement
I have multiple applications and the are for example, MVC/SPA/Mobile etc. I wish to allow each application to be a client and call an API.
I need a setup where applications can call a protected API without a user being logged in. So the API is protected based on scopes.
I know I can use client_credentials flow to get access_token and call my API. Which is fine for apps like the MVC as the secret will never be exposed.
I read that this flow is not recommended for mobile/SPA apps and to use Authorization Code flow, but I can't seem to get a token without it asking to login as a User.
Question
What other flow can I use to get only access_token without logging in the user and keeping my app secure?
Or, should I just use client_credential flow in my mobile apps and SPA's?

Access Token for both Microsoft Graph and Custom API

I have a ReactJs frontend making requests to an API. Both hosted in Azure with app registrations in AAD as well.
I used to be able to use v1.0 auth endpoint, and create a valid token for the API:
https://login.microsoftonline.com/common/oauth2/authorize?client_id=<AAD_WEB_APP_ID>&resource=<AAD_API_ID>&response_type=token ...
If I understand the documentation correctly, this type of auth flow isn't allowed/possible in v2.0:
However, that Web API can receive tokens only from an application that has the same Application ID. You cannot access a Web API from a client that has a different Application ID. The client won't be able to request or obtain permissions to your Web API.
The reason for changing from v1.0 to v2.0 is that I need access to Microsoft Graph (Groups in particular).
My question is: How can I create an access_token that works for Microsoft Graph and my API? If that isn't possible, what would the correct auth flow be?
You don't need to switch to the v2 Endpoint for this, Microsoft Graph supports both v1 and v2 tokens (actually, every API I can think of that supports v2 also supports v1 but there might be an exception I'm forgetting).
The steps are pretty straightforward:
Update your AAD registration in the Azure Portal and add the Permissions for Microsoft Graph you're going to be using.
Instead of passing resource=<AAD_API_ID> in your URI, use resource=graph.microsoft.com. This will return a token that can be used with Microsoft Graph.
Important: You must request the Offline Access scope (offline_access) for this to work.
Where this gets confusing is that technically you cannot use the same Access Token to access both your API and Microsoft Graph. What is supported is switching the Resource when refreshing your token. So while, yes, you are using two different tokens, you're reusing the same credentials/authorization code.
Here is an example flow:
A user authenticates using your API as the Resource (resource=<AAD_API_ID>). This returns an Authorization Code back to your application.
The application posts the Authorization Code to the /token endpoint (also using your API as the Resource). This will return both an access_token and a refresh_token to the application.
Use this access_token to make calls into your API.
The application posts the refresh_token to the /token endpoint using graph.microsoft.com as the Resource. This will return a new access_token and refresh_token keyed to Microsoft Graph.
Use this new access_token to make calls into Microsoft Graph.
The application again posts the refresh_token to the /token endpoint but this time using your API as the Resource again. This will return a new access_token and refresh_token keyed to your API.
Call your API
You can repeat this cycle as needed. Depending on how often you need to switch, you can also keep access tokens for both your API and Graph in memory and reuse them until they expire. Just be sure and always store the last Refresh Token you received so you can fetch a refreshed token for either resource as needed.

Resources