Open cannikin opened 4 years ago
This is what I have been doing with Auth0 (which could also be setup to do hooks on signup/signin etc ... in fact).
That said: the webhook approach is actually a much better implementation since it would not clutter getCurrentUser
with something that only happens once it a while ie, sign up
... and a webhook on sign in
would keep the User record up-to-date.
import { AuthenticationClient } from 'auth0'
import { AuthenticationError } from '@redwoodjs/api'
import { context } from '@redwoodjs/api/dist/globalContext'
import { db } from 'src/lib/db'
const auth0 = new AuthenticationClient({
domain: process.env.AUTH0_DOMAIN,
clientId: process.env.AUTH0_CLIENT_ID,
})
export const getCurrentUser = async (decoded, { authType, token }) => {
// shortcut if we have a user profile in context
// note: if something else changes the User record
// can't rely on this shortcut but will reduce db calls
if (context.currentUser?.userId) {
return context.currentUser
}
// do we have an accessToken from auth0 and an userId from the decoded JWT?
if (!token || authType != 'auth0' || !decoded?.sub) {
return decoded
}
// find the existing User ...
// or create a new User with its userProfile info
try {
const user = await db.user.findOne({
where: {
userId: decoded.sub,
},
})
if (!user && token) {
const auth0User = await auth0.getProfile(token)
const userProfile = {
email: auth0User.email,
emailVerified: auth0User.emailVerified,
lastIp: auth0User.lastIp,
lastLogin: auth0User.lastLogin,
loginsCount: auth0User.loginsCount,
name: auth0User.name,
nickname: auth0User.nickname,
picture: auth0User.picture,
userId: auth0User.sub,
}
const userWithProfile = await db.user.create({
data: userProfile,
})
// set the currentUser in context to include its userProfile info
const currentUser = context.currentUser
context.currentUser = { currentUser, ...userProfile }
return userWithProfile
}
return user
} catch (error) {
console.log(error)
return decoded
}
}
with a User model:
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
emailVerified Boolean?
lastIp String?
lastLogin DateTime @default(now())
loginsCount Int @default(0)
name String
nickname String
picture String
userId String @unique
}
and users service:
import { db } from 'src/lib/db'
export const users = () => {
return db.user.findMany()
}
export const user = ({ id }) => {
return db.user.findOne({
where: { id },
})
}
export const userByUserId = ({ userId }) => {
return db.user.findOne({
where: { userId },
})
}
export const createUser = ({ input }) => {
return db.user.create({
data: input,
})
}
...
Show how to create a user in your local DB as the result of a user signing up on a third party service. Simplest example would be with Netlify Identity and creating the user if they don't exist when you try to find them in
api/src/lib/auth.js#getCurrentUser
but the exact same instructions would work with any auth provider that returns an email address in their JWT response.Netlify Identity also supports webhooks when a new user signs up, we could include this as an alternate method of user creation (and as a bonus also shows people how to create a custom function that uses existing services).
See https://github.com/redwoodjs/redwood/issues/745 and https://community.redwoodjs.com/t/auth-what-about-signing-up/796/2