DEV Community

Cover image for Serverless GitHub OAuth with OpenJS Architect and Begin - Part 2
Paul Chin Jr.
Paul Chin Jr.

Posted on

Serverless GitHub OAuth with OpenJS Architect and Begin - Part 2

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.

@app
github-oauth

@static

@http
get /login
get /auth
post /logout

@tables
data
  scopeID *String
  dataID **String
  ttl TTL
Enter fullscreen mode Exit fullscreen mode

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.

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)
Enter fullscreen mode Exit fullscreen mode

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.

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)
Enter fullscreen mode Exit fullscreen mode

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.

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)