3

I have a scenario where I need to implement OAuth 2.0 authorization code flow on the server side - the user authorizes a backend service against Microsoft, the service receives a token, saves it in a database and uses it to perform daily background jobs on the user's data. The token itself is neither needed nor received in the frontend app.

The backend service is an asp.net 5 app with an api and a hosted service. The frontend app only does the initial request to the api, which results in a redirect to Microsoft. The oauth callback url is pointed to the api endpoint which receives the code, gets a token and redirects to the frontend app upon success/failure.

The question is, how to implement state parameter to avoid CSRF attacks and be able to restore some user state in this scenario? E.g. to prevent someone posting some else's code to the callback endpoint. In the usual code flow, the frontend app would generate a state parameter and store it in local storage together with some user state (described here), and compare the state value in the redirect upon returning to the frontend app.

In my case it is the api that needs to generate a state parameter, store it and check it in the callback url. I don't have a user session and the api is load balanced, so I need an external storage, but the solution should be similar. My plan was:

  • In the endpoint that redirects to oauth2 login, generate a guid that acts as the state,
  • Store it in some external cache as the key with the value of some extra user data (id, name, etc) that the endpoint receives,
  • Pass the guid in the state query parameter (?state=asd123) to the redirect url,
  • In the callback endpoint retrieve the state and check whether such a key exists in the cache,
  • If it is, the request is valid and I can use the extra data attached to the key.

All the tutorials I could find deal with the frontend app and local storage, so my idea feels hackish and I wouldn't want to risk when it comes to security by implementing some random algorithms. Do I need to pass that extra user data in state or is it fine to rely on it being present in the cache if the request is valid? Is there a more standard secure way of doing this?

Here is a sequence diagram of the described login flow in case it is helpful: Code flow sequence diagram

2
  • I'm not sure if for this scenario, you should be using OAuth 2.0 Code Flow, it is possible you need a Client Credentials flow instead? given that the interaction you need is between your server and the Microsoft API, seems user involvement is not needed at all, of course, this depends on what are you trying to do with Microsoft API, if you can clarify that, happy to answer. Commented Jan 20, 2022 at 20:04
  • The user needs to login with his microsoft email first so that we can get an access token, which we use in a background job to get the user's data from the MS graph api. So the user still needs to be there in the pipeline, only the very first time when he gives our app consent to access certain data from the MS graph api. Commented Jan 21, 2022 at 22:25

1 Answer 1

0

I believe this implementation is not secure against certain type of CSRF attack. Consider:

Actors:

  • Mallory and Alice both have accounts in your app
  • Alice has Microsoft account

The following flow will end up linking app account of Mallory to Microsoft account of Alice

Flow:

  1. Mallory starts the flow and gets redirected to authorization page (Microsoft login) with state=Mallory
  2. Mallory copies the link and tricks Alice into following it and completing it
  3. Microsoft does request to your callback endpoint with state=Mallory and code for Alice
  4. Since state=Mallory does exist in cache, it passes the check
  5. Since state=Mallory is used to identify the app account, the application links app account of Mallory to Microsoft account of Alice

In real implementation state=Mallory is a guid that points to info about Mallory in the cache

From RFC 6749 we can infer that state is meant to by tied to the user-agent of user who completes authorization page (emphasis mine):

The client MUST implement CSRF protection for its redirection URI. This is typically accomplished by requiring any request sent to the redirection URI endpoint to include a value that binds the request to the user-agent's authenticated state (e.g., a hash of the session cookie used to authenticate the user-agent)

And then:

Once authorization has been obtained from the end-user, the authorization server redirects the end-user's user-agent back to the client with the required binding value contained in the "state" parameter. The binding value enables the client to verify the validity of the request by matching the binding value to the user-agent's authenticated state

Although I am not sure exactly what "user-agent authenticated" means

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.