How to Identify the user who gave Authorization permission?

Viewed 52

Heres my scenario

-> front end React

-> backend Node

I am carrying out user authentication using JWT ie, everytime frontend needs anything it requests backend with Authorization header and a bearer token.

My doubt

I have a user x on my site and for a use case i need him to link his github account to his already existing account on my site.

For that he clicks on link github button and goes to https://github.com/login/oauth/authorize?client_id=xxxxxxx, and gives the permission to share his data and gets redirected to callback link provided when creating oauth app on github.

The callback route on my server will receive a code that can later be used to access a token that inturn can be used to access the GitHub account on behalf of the user.

But the doubt is how do i know to which user this github callback code belongs to ? Wheather it belongs to users x or someone else ?

In normal scenario JWT could help in identifying the user but here since the callback is requested from GitHub there is no JWT token, so no chance to identify the user!

I hope the question is clear, thanks in advance for helping!

2 Answers

As part of the user authorization flow in GitHub, you can use the state parameter to send any random data plus the user id from your system that initiated the GitHub authorization flow. This state parameter should contain a random string to protect against forgery attacks and could contain any other arbitrary data (like your system user id), then when handling the authorization callback in your application, GitHub sends that state parameter back to your application. If the states don't match, the request was created by a third party and the process should be aborted.

You can read more about how GitHub OAuth Apps works here Also, if you want to understand a little more about the state param as part of the OAuth 2.0 Authorization framework spec, read more about it here

In your backend, when you're handling the callback, you have now an access token returned from GitHub after the user authorization flow, with that, you can fetch the authorizing user information and associate the user id from GitHub with your system user id that you got from the state parameter, here is an example code of that:

See an example implementation in action                                                                                 Run in Fusebit
import superagent from 'superagent';

// This code should be placed where you are handling the callback in your backend
// Put the access token from the callback response here
const access_token = '';
const userResponse = await superagent
  .get('https://api.github.com/user')
  .set('User-Agent', 'my-app')
  .set('Authorization', `Bearer ${access_token}`)
  .set('Accept', 'application/vnd.github.v3+json');

// Get the GitHub user id and connect it with the id you have in the state parameter
const gitHubUserId = userResponse.body.id;

The callback response body will have something like:

{
  "access_token": "ghu_16C7e42F292c6912E7710c838347Ae178B4a",
  "expires_in": 28800,
  "refresh_token": "ghr_1B4a2e77838347a7E420ce178F2E7c6912E169246c34E1ccbF66C46812d16D5B1A9Dc86A1498",
  "refresh_token_expires_in": 15811200,
  "scope": "",
  "token_type": "bearer"
}

The user response will be something like:

{
  "login": "octocat",
  "id": 1,
  "node_id": "MDQ6VXNlcjE=",
  "avatar_url": "https://github.com/images/error/octocat_happy.gif",
  "gravatar_id": "",
  "url": "https://api.github.com/users/octocat",
  "html_url": "https://github.com/octocat",
  "followers_url": "https://api.github.com/users/octocat/followers",
  "following_url": "https://api.github.com/users/octocat/following{/other_user}",
  "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
  "organizations_url": "https://api.github.com/users/octocat/orgs",
  "repos_url": "https://api.github.com/users/octocat/repos",
  "events_url": "https://api.github.com/users/octocat/events{/privacy}",
  "received_events_url": "https://api.github.com/users/octocat/received_events",
  "type": "User",
  "site_admin": false,
  "name": "monalisa octocat",
  "company": "GitHub",
  "blog": "https://github.com/blog",
  "location": "San Francisco",
  "email": "octocat@github.com",
  "hireable": false,
  "bio": "There once was...",
  "twitter_username": "monatheoctocat",
  "public_repos": 2,
  "public_gists": 1,
  "followers": 20,
  "following": 0,
  "created_at": "2008-01-14T04:33:35Z",
  "updated_at": "2008-01-14T04:33:35Z",
  "private_gists": 81,
  "total_private_repos": 100,
  "owned_private_repos": 100,
  "disk_usage": 10000,
  "collaborators": 8,
  "two_factor_authentication": true,
  "plan": {
    "name": "Medium",
    "space": 400,
    "private_repos": 20,
    "collaborators": 0
  }
}

It is the users own browser that makes the final redirect back from Github and passes the authorization code to your client backend application.

GitHub never contacts your client directly. It is all done through the users browser.

So, that is how the client knows who the code belongs to. Also there is the state and PKCE security features in the protocol that further improves the security, that only the client application making the initial request is the same as the one completing the flow.

Related