Serverless GitHub OAuth with OpenJS Architect and Begin - Part 2
Tags: aws, cloud, serverless, architect
Published: 2020-12-03 16:53:54

In the previous post, we deployed a full login example and now we'll follow the execution of the Lambdas when the app starts. 

## Project structure and Hello Infra
Notice that there's an `app.arc` file, this is our cloud resource manifest. Architect parses this file to generate the CloudFormation you will need to deploy. From this one file, we can see the structure of the project and where the code is organized. 

```bash
@app
github-oauth

@static

@http
get /login
get /auth
post /logout

@tables
data
  scopeID *String
  dataID **String
  ttl TTL
``` 
The `@http` pragma tells us the route and method for our HTTP endpoints for the whole application. `@static` tells us there are static assets in the `/public` folder that are served from an S3 bucket. 

## Application flow 
When you first load the app, the frontend makes a GET request to `/auth` to retrieve a login link for authentication. It will also return account data if it is available. Let's take a look at the `get-auth` Lambda function. 
```javascript
const arc = require('@architect/functions')

async function auth(req) {
  let account = req.session &&
    req.session.account
  let clientID = process.env.GITHUB_CLIENT_ID
  let redirectURL = process.env.GITHUB_REDIRECT
  let href = `https://github.com/login/oauth/authorize?client_id=${clientID}&redirect_url=${redirectURL}`
  let payload = {
    account,
    href
  }

  return {
    body: JSON.stringify(payload)
  }
}

exports.handler = arc.http.async(auth)
```
First, we attempt to read the account from the session.

Then, we construct a GitHub login URL with the secret client ID and redirect URL from the GitHub OAuth app we set up previously

Finally, we return the login URL and account data if available.

Once the frontend button has a full authorization link, the user will be directed to sign in and authorize the application to load their account details. 

## `get-login` Lambda function
This get-login function is where our GitHub app redirects to after successfully authenticating.

If we have successfully authenticated we can then use the returned code to retrieve the account data from GitHub's API.

```javascript
const arc = require('@architect/functions')
const github = require('./github')

async function login(req) {
  let account
  if (req.query.code) {
    try {
      account = await github(req)
    } catch (err) {
      return {
        statusCode: err.code,
        body: err.message
      }
    }
    return {
      session: {account},
      location: '/'
    }
  } else {
    return {
      location: '/'
    }
  }
}

exports.handler = arc.http.async(login)
```
We check for req.query.code

Then use the code to retrieve the user account from the GitHub API

Finally, we return the account if present.

## `get-login/github.js`
This github.js file is used to retrieve the account data from GitHub.

First, we POST to the GitHub OAuth service with the authentication code to retrieve an access token

Then we retrieve the account data with the access token set as an Authorization Header.

Finally, we return the account data or any error we receive.

```javascript
const tiny = require('tiny-json-http')

module.exports = async function github(req) {
  try {
    let result = await tiny.post({
      url: 'https://github.com/login/oauth/access_token',
      headers: {Accept: 'application/json'},
      data: {
        code: req.query.code,
        client_id: process.env.GITHUB_CLIENT_ID,
        client_secret: process.env.GITHUB_CLIENT_SECRET,
        redirect_url: process.env.GITHUB_REDIRECT
      }
    })
    let token = result.body.access_token
    let user = await tiny.get({
      url: `https://api.github.com/user`,
      headers: {
        Authorization: `token ${token}`,
        Accept: 'application/json'
      }
    })
    return {
      token,
      name: user.body.name,
      login: user.body.login,
      id: user.body.id,
      url: user.body.url,
      avatar: user.body.avatar_url
    }
  } catch (err) {
    return {
      error: err.message
    }
  }
}
```
## Summary 
GitHub OAuth has the following steps: 
1. Create an authorization URL with your GitHub client ID and redirect URL
2. Have users sign in with that link 
3. Receive an authorization token that is used to exchange for account information.