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
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)
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)
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
}
}
}
Summary
GitHub OAuth has the following steps:
- Create an authorization URL with your GitHub client ID and redirect URL
- Have users sign in with that link
- Receive an authorization token that is used to exchange for account information.
Top comments (0)