Custom Auth Login + verify invite token

This commit is contained in:
Barry Low committed 2018-09-28 08:49:33 -07:00
1 parent caba8bb338
commit 4ca444addf
9 files changed
+162 -13

No files matched your search

+19 -2
View File
@@ -7,7 +7,7 @@ const sendTeamMemberInvite = function(email, team) {
const API_KEY = process.env.SG_APIKEY
const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' })
let origin = 'https://teams.advancedalgos.net'
const params = '/activate-team-membership?token='
const params = '/activate-team-membership/'
if (dev){
origin = 'http://localhost:3001'
}
@@ -67,4 +67,21 @@ const sendTeamMemberInvite = function(email, team) {
return sendVerify
}
module.exports = { sendTeamMemberInvite }
const verifyInviteToken = async function(token) {
const API_KEY = process.env.SG_APIKEY
let verifiedToken
try {
verifiedToken = await jwt.verify(token, API_KEY, {maxAge: '7d'})
} catch(err) {
if (err.name === "TokenExpiredError"){
throw new Error ("Error: Token Expired. Please request new invitation or apply to the team again")
} else {
throw new Error (`Error: Token error - ${err.message}`)
}
}
console.log('verifyInviteToken', verifiedToken)
return verifiedToken
}
module.exports = { sendTeamMemberInvite, verifyInviteToken }
+19 -1
View File
@@ -14,7 +14,7 @@ const { getMember } = require('./auth/middleware/getMember')
const { validateIdToken } = require('./auth/validateIdToken')
const { directiveResolvers } = require('./auth/authDirectives')
const { sendTeamMemberInvite } = require('./email/sendgrid')
const { sendTeamMemberInvite, verifyInviteToken } = require('./email/sendgrid')
const {
createStoragePipline,
@@ -136,6 +136,24 @@ const resolvers = {
}
return member
},
async verifyTeamInvite(parent, { token }, ctx, info) {
let verifiedToken = null
let team = null
try {
verifiedToken = await verifyInviteToken(token)
team = {
email: verifiedToken.email,
team: {
slug: verifiedToken.team
}
}
console.log('verifyTeamInvite.token: ', await verifiedToken)
} catch (err) {
console.log('verifyTeamInvite. err: ', err)
throw new Error(err.message)
}
return team
},
async createTeam(parent, { name, slug }, ctx, info) {
const authId = ctx.request.user.sub
return ctx.db.mutation.createTeam({ data: { name: name, slug: slug, owner: authId, members: { create: { member:{ connect:{ authId: authId} }, role: 'OWNER' } }, profile:{create:{avatar:"a"} } } }, TEAMS_FRAGMENT)
+1
View File
@@ -20,6 +20,7 @@ type Query {
type Mutation {
authenticate(idToken: String!): Member
verifyTeamInvite(token: String!): TeamMembers
createTeam(name: String!, slug: String!): Team @isAuthenticated
updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated
deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team")
+25 -8
View File
@@ -6266,8 +6266,7 @@
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"decompress-response": {
"version": "3.3.0",
@@ -12102,6 +12101,18 @@
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
},
"dependencies": {
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"dev": true,
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
}
},
"npm-run-all": {
@@ -13791,13 +13802,19 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"dev": true,
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.1.0.tgz",
"integrity": "sha512-pNB/Gr8SA8ff8KpUFM36o/WFAlthgaThka5bV19AD9PNTH20Pwq5Zxodif2YyHwrctp6SkL4GqlOot0qR/wGaw==",
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"decode-uri-component": "^0.2.0",
"strict-uri-encode": "^2.0.0"
},
"dependencies": {
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
}
}
},
"querystring": {
+1
View File
@@ -89,6 +89,7 @@
"graphql": "^0.13.2",
"graphql-tag": "^2.9.2",
"prop-types": "^15.6.2",
"query-string": "^6.1.0",
"react": "^16.5.0",
"react-apollo": "^2.1.11",
"react-dom": "^16.5.0",
+6
View File
@@ -19,6 +19,7 @@ import {
NavBar,
Footer,
Callback,
AcceptTeamInvite,
Home,
About,
Dashboard,
@@ -171,6 +172,11 @@ class App extends Component {
return <Callback {...props} />
}}
/>
<Route
exact
path='/activate-team-membership'
render={props => <AcceptTeamInvite {...props} auth={auth} />}
/>
</Switch>
<Footer />
</div>
+58 -2
View File
@@ -16,7 +16,19 @@ const AUTHENTICATE = gql`
}
}
`
const lock = new Auth0Lock(AUTH_CONFIG.clientId, AUTH_CONFIG.domain, {
const VERIFY_TEAM_INVITE = gql`
mutation verifyTeamInvite($token: String!) {
verifyTeamInvite(token: $token) {
email
team {
slug
}
}
}
`
export const defaultOptions = {
oidcConformant: true,
autoclose: true,
auth: {
@@ -28,7 +40,27 @@ const lock = new Auth0Lock(AUTH_CONFIG.clientId, AUTH_CONFIG.domain, {
scope: `openid profile email user_metadata app_metadata picture`
}
}
})
}
export const inviteOptions = {
oidcConformant: true,
autoclose: false,
allowedConnections: ['Username-Password-Authentication'],
allowShowPassword: true,
auth: {
sso: false,
redirectUrl: window.location.origin + '/callback',
responseType: 'token id_token',
audience: `${AUTH_CONFIG.api_audience}`,
params: {
state: '',
scope: `email`
}
},
prefill: { email: '' }
}
let lock = new Auth0Lock(AUTH_CONFIG.clientId, AUTH_CONFIG.domain, defaultOptions)
class Auth {
constructor (cb, apolloClient) {
@@ -37,6 +69,7 @@ class Auth {
this.apolloClient = apolloClient
this.cb = cb.bind(this)
this.login = this.login.bind(this)
this.loginInvite = this.loginInvite.bind(this)
this.logout = this.logout.bind(this)
this.isAuthenticated = this.isAuthenticated.bind(this)
this.checkSession = this.checkSession.bind(this)
@@ -47,6 +80,29 @@ class Auth {
lock.show()
}
async loginInvite (jwt) {
let email
let team
try {
const data = await client.mutate({
mutation: VERIFY_TEAM_INVITE,
variables: { token: jwt }
})
console.log('loginInvite auth: ', await data.data.verifyTeamInvite)
setItem('invite', JSON.stringify(data.data.verifyTeamInvite))
email = data.data.verifyTeamInvite.email
team = data.data.verifyTeamInvite.team.slug
} catch (err) {
return console.log('loginInvite err: ', err)
}
inviteOptions.prefill.email = email
inviteOptions.auth.params.state = `${email}|${team}`
let lockInvite = new Auth0Lock(AUTH_CONFIG.clientId, AUTH_CONFIG.domain, inviteOptions)
lockInvite.show()
}
handleAuthentication () {
// Add a callback for Lock's `authenticated` event
lock.on('authenticated', this.setSession.bind(this))
+2
View File
@@ -5,6 +5,7 @@ import Home from './home'
import About from './about'
import Dashboard from './dashboard'
import Teams from './teams'
import AcceptTeamInvite from './teams/AcceptTeamInvite'
import { theme, globalStyles } from './theme'
@@ -12,6 +13,7 @@ export {
NavBar,
Footer,
Callback,
AcceptTeamInvite,
Home,
About,
Dashboard,
@@ -0,0 +1,31 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import queryString from 'query-string'
import { MessageCard } from '../common/'
class AcceptTeamInvite extends Component {
render () {
const { location, auth } = this.props
const param = queryString.parse(location.search)
console.log(param, auth)
if (auth === undefined) {
return <MessageCard message='Loading...' />
}
auth.loginInvite(param.token)
return (
<MessageCard message='Verifying Invitation' />
)
}
}
AcceptTeamInvite.propTypes = {
location: PropTypes.object.isRequired,
auth: PropTypes.object
}
export default AcceptTeamInvite