summaryrefslogtreecommitdiffstats
path: root/src/auth0/auth0Authentication.js
blob: f35cfd4f47577c0620594e5a28e61c53a3df25fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const isomorphicFetch = require('isomorphic-fetch')
const jwt = require('jsonwebtoken')
const jwkRsa = require('jwks-rsa')
const fromEvent = require('graphcool-lib').fromEvent

//Validates the request JWT token
const verifyToken = token =>
  new Promise(resolve => {
    //Decode the JWT Token
    const decoded = jwt.decode(token, { complete: true })
    if (!decoded || !decoded.header || !decoded.header.kid) {
      throw new Error('Unable to retrieve key identifier from token')
    }
    if (decoded.header.alg !== 'RS256') {
      throw new Error(
        `Wrong signature algorithm, expected RS256, got ${decoded.header.alg}`
      )
    }
    const jkwsClient = jwkRsa({
      cache: true,
      jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
    })
    //Retrieve the JKWS's signing key using the decode token's key identifier (kid)
    jkwsClient.getSigningKey(decoded.header.kid, (err, key) => {
      if (err) throw new Error(err)
      const signingKey = key.publicKey || key.rsaPublicKey
      //If the JWT Token was valid, verify its validity against the JKWS's signing key
      jwt.verify(
        token,
        signingKey,
        {
          algorithms: ['RS256'],
          audience: process.env.AUTH0_API_IDENTIFIER,
          ignoreExpiration: false,
          issuer: `https://${process.env.AUTH0_DOMAIN}/`
        },
        (err, decoded) => {
          if (err) throw new Error(err)
          return resolve(decoded)
        }
      )
    })
  })

//Retrieves the Graphcool user record using the Auth0 user id
const getGraphcoolUser = (auth0UserId, api) =>
  api
    .request(
      `
        query getUser($auth0UserId: String!){
          User(auth0UserId: $auth0UserId){
            id
          }
        }
      `,
      { auth0UserId }
    )
    .then(queryResult => queryResult.User)

//Creates a new User record.
const createGraphCoolUser = (auth0UserId, email, api) =>
  api
    .request(
      `
        mutation createUser($auth0UserId: String!, $email: String) {
          createUser(
            auth0UserId: $auth0UserId
            email: $email
          ){
            id
          }
        }
      `,
      { auth0UserId, email }
    )
    .then(queryResult => queryResult.createUser)

const fetchAuth0Email = accessToken =>
  fetch(
    `https://${process.env.AUTH0_DOMAIN}/userinfo?access_token=${accessToken}`
  )
    .then(response => response.json())
    .then(json => json.email)

export default async event => {
  try {
    if (!process.env.AUTH0_DOMAIN || !process.env.AUTH0_API_IDENTIFIER) {
      throw new Error(
        'Missing AUTH0_DOMAIN or AUTH0_API_IDENTIFIER environment variable'
      )
    }
    const { accessToken } = event.data

    const decodedToken = await verifyToken(accessToken)
    const graphcool = fromEvent(event)
    const api = graphcool.api('simple/v1')

    let graphCoolUser = await getGraphcoolUser(decodedToken.sub, api)
    //If the user doesn't exist, a new record is created.
    if (graphCoolUser === null) {
      // fetch email if scope includes it
      let email = null
      if (decodedToken.scope.includes('email')) {
        email = await fetchAuth0Email(accessToken)
      }
      graphCoolUser = await createGraphCoolUser(decodedToken.sub, email, api)
    }

    // custom exp does not work yet, see https://github.com/graphcool/graphcool-lib/issues/19
    const token = await graphcool.generateNodeToken(
      graphCoolUser.id,
      'User',
      decodedToken.exp
    )

    return { data: { id: graphCoolUser.id, token } }
  } catch (err) {
    console.log(err)
    return { error: 'An unexpected error occured' }
  }
}