Skip to content

Instantly share code, notes, and snippets.

@kishino
Created September 27, 2019 03:38
Show Gist options
  • Save kishino/281f13461e975c819161298d35b6de54 to your computer and use it in GitHub Desktop.
Save kishino/281f13461e975c819161298d35b6de54 to your computer and use it in GitHub Desktop.
apple-login-sample-server.js
const express = require('express')
const app = express()
const fs = require('fs')
const cors = require('cors')
const Datastore = require('nedb')
const bodyParser = require('body-parser')
const jwt = require('jsonwebtoken')
const jwksClient = require('jwks-rsa')
const axios = require('axios')
const qs = require('qs')
const UIDGenerator = require('uid-generator')
const uidgen = new UIDGenerator()
app.use(express.static('www'))
app.use(cors())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json())
const db = new Datastore({
filename: 'datastore',
autoload: true
})
app.post('/signup', async (req, res) => {
try {
const payload = await getIdTokenPayload(req.body.code)
const userId = payload.sub
db.find({ userId }, async (err, ret) => {
let user = ret[0]
// ユーザーがすでに存在する場合エラー
if (user) {
res.status(400).json({
message: 'すでに登録済みです。'
})
return
}
// メールアドレスがない or 未検証の場合エラー
if (!payload.email || payload.email_verified !== 'true') {
res.status(400).json({
message: 'メールアドレスが取得できませんでした。'
})
return
}
// ユーザーを登録
user = {
userId,
email: payload.email,
name: req.body.userName,
token: await uidgen.generate()
}
db.insert(user)
res.json(user)
})
} catch (e) {
console.error(e)
res.status(404).json({
message: 'エラーが発生しました'
})
}
})
app.post('/login', async (req, res) => {
try {
const payload = await getIdTokenPayload(req.body.code)
const userId = payload.sub
db.find({ userId }, function(err, ret) {
let user = ret[0]
// ユーザーが存在しない場合エラー
if (!user) {
res.status(400).json({
message: 'このユーザーは存在しません'
})
return
}
// メールを取得できた場合は更新
if (payload.email && payload.email_verified === 'true') {
const id = user._id
delete user._id
db.update({ _id: id } , user)
}
res.json(user)
})
} catch (e) {
console.error(e)
res.status(404).json({
message: 'エラーが発生しました'
})
}
})
app.get('/user', async (req, res) => {
try {
const token = req.header('X-Token')
db.find({ token }, function(err, ret) {
let user = ret[0]
// ユーザーが存在しない場合エラー
if (!user) {
res.status(400).json({
message: 'このユーザーは存在しません。'
})
return
}
res.json(user)
})
} catch (e) {
console.error(e)
res.status(404).json({
message: 'エラーが発生しました'
})
}
})
async function getIdTokenPayload(code) {
// ID Tokenを発行
const resp = await axios.post('https://appleid.apple.com/auth/token', qs.stringify({
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
code,
grant_type: 'authorization_code'
}))
const idToken = resp.data.id_token
const client = jwksClient({
jwksUri: 'https://appleid.apple.com/auth/keys'
})
// Appleから公開鍵を取得
const getKey = (header, callback) => {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.publicKey || key.rsaPublicKey
callback(null, signingKey)
})
}
// ID Tokenの署名+内容(有効期限、発行者、発行先など)を検証
return new Promise((resolve, reject) => {
jwt.verify(idToken, getKey, {
issuer: 'https://appleid.apple.com',
audience: process.env.CLIENT_ID
}, function(err, decoded) {
if (err) {
reject(err)
} else {
console.info(decoded)
resolve(decoded)
}
})
})
}
const port = 3000
const listener = app.listen(port, function() {
console.log('Your app is listening on port ' + listener.address().port)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment