New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auth Hooks #1993
base: main
Are you sure you want to change the base?
Auth Hooks #1993
Conversation
@@ -0,0 +1,30 @@ | |||
import type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example usage of the hooks
@@ -40,7 +40,11 @@ app todoApp { | |||
}, | |||
}, | |||
onAuthFailedRedirectTo: "/login", | |||
onAuthSucceededRedirectTo: "/profile" | |||
onAuthSucceededRedirectTo: "/profile", | |||
onBeforeSignup: import { onBeforeSignup } from "@src/auth/hooks.js", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declare the hooks in the auth
dict
const { accessToken } = await github.validateAuthorizationCode(code); | ||
return getGithubProfile(accessToken); | ||
}, | ||
getProviderTokens: ({ code }) => github.validateAuthorizationCode(code), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor OAuth a bit to split getting tokens and getting user profile into two steps so it's easier to write a hook onAfterOAuthTokenReceived
import type { ProviderId, createUser } from '../../auth/utils.js' | ||
import { prisma } from '../index.js' | ||
|
||
type CommonInput = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each hook will receive hookName
, prisma
and Express's req
object.
} & CommonInput | ||
|
||
/* On Before OAuth Redirect Hook */ | ||
export type OnBeforeOAuthRedirectHookFn = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Users have the ability to modify the OAuth redirect URL
const user = await createUser( | ||
providerId, | ||
providerData, | ||
// Using any here because we want to avoid TypeScript errors and | ||
// rely on Prisma to validate the data. | ||
userFields as any, | ||
) | ||
if (onAfterSignupHook) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook
@@ -34,13 +35,19 @@ export function getSignupRoute({ | |||
}) | |||
|
|||
try { | |||
await createUser( | |||
if (onBeforeSignupHook) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook
providerId, | ||
providerData, | ||
// Using any here because we want to avoid TypeScript errors and | ||
// rely on Prisma to validate the data. | ||
userFields as any | ||
) | ||
if (onAfterSignupHook) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook
passwordResetSentAt: null, | ||
}); | ||
try { | ||
if (onBeforeSignupHook) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook
// rely on Prisma to validate the data. | ||
userFields as any, | ||
) | ||
if (onAfterSignupHook) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook
72dc06f
to
ff5ce18
Compare
" methods: {", | ||
" google: {}", | ||
" },", | ||
" onAuthFailedRedirectTo: \"/login\"", | ||
" onAuthFailedRedirectTo: \"/login\",", | ||
" onBeforeSignup: import { onBeforeSignup } from \"@src/auth/hooks.js\",", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've included hooks in the e2e test
unlines | ||
[ "entity SocialLogin {=psl", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've cleaned up the e2e tests not use old auth concepts 👍
```ts title="src/auth/hooks.ts" | ||
import type { OnBeforeSignupHookFn } from 'wasp/server/auth' | ||
|
||
export const onBeforeSignup: OnBeforeSignupHookFn = async ({ providerId, prisma, req, hookName }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: add better example e.g. throw a HttpError
to show an error message on the client.
web/docs/auth/auth-hooks.md
Outdated
|
||
```js title="src/auth/hooks.js" | ||
export const onAfterSignup = async ({ providerId, user, prisma, req, hookName }) => { | ||
// Do something after signup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: add a better example e.g. set a field on the user entity or send data to an external service.
web/docs/auth/auth-hooks.md
Outdated
|
||
```js title="src/auth/hooks.js" | ||
export const onBeforeOAuthRedirect = async ({ url, prisma, req, hookName }) => { | ||
// Do something before OAuth redirect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: show a useful example of setting something in the query params and getting it back in the callback.
2f39592
to
382f643
Compare
@sodic and I jumped on quick call to discuss some of the OAuth hooks since I felt I didn't design them to be that useful. We concluded the following:
|
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
382f643
to
9bcd660
Compare
@@ -77,13 +77,11 @@ const _waspConfig: ProviderConfig = { | |||
|
|||
return createOAuthProviderRouter({ | |||
provider, | |||
stateTypes: ['state'], | |||
optionalStateTypes: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the state
is always sent and we rely on that knowledge in the new hooks, I've refactored this a bit to reflect the fact that the state
will be always present in the oAuthState object.
setOAuthCookieValue(provider, res, 'state', state); | ||
result.state = state; | ||
} | ||
const state = generateState(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always set the state since it's always used and will always be used for each future OAuth provider.
Closes #1556
Adding hooks:
onAfterOAuthTokenReceivedLeft to do: