Skip to content
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

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open

Auth Hooks #1993

wants to merge 18 commits into from

Conversation

infomiho
Copy link
Contributor

@infomiho infomiho commented Apr 24, 2024

Closes #1556

Adding hooks:

  • onBeforeSignup
  • onAfterSignup
  • onBeforeOAuthRedirect
  • onAfterOAuthTokenReceived

Left to do:

@@ -0,0 +1,30 @@
import type {
Copy link
Contributor Author

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",
Copy link
Contributor Author

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),
Copy link
Contributor Author

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 = {
Copy link
Contributor Author

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 = (
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hook

@infomiho infomiho marked this pull request as ready for review May 2, 2024 12:25
@infomiho infomiho requested a review from sodic May 3, 2024 11:41
" methods: {",
" google: {}",
" },",
" onAuthFailedRedirectTo: \"/login\"",
" onAuthFailedRedirectTo: \"/login\",",
" onBeforeSignup: import { onBeforeSignup } from \"@src/auth/hooks.js\",",
Copy link
Contributor Author

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",
Copy link
Contributor Author

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 }) => {
Copy link
Contributor Author

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.


```js title="src/auth/hooks.js"
export const onAfterSignup = async ({ providerId, user, prisma, req, hookName }) => {
// Do something after signup
Copy link
Contributor Author

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.


```js title="src/auth/hooks.js"
export const onBeforeOAuthRedirect = async ({ url, prisma, req, hookName }) => {
// Do something before OAuth redirect
Copy link
Contributor Author

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.

@infomiho
Copy link
Contributor Author

infomiho commented May 9, 2024

@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:

  • we should remove the onAfterOAuthTokenReceived hook since the developer doesn't have access to user and the accessToken at the same time. This makes the accessToken useless if you want to save it on some user for later use.
    • Solution: We'll just forward the accessToken in the onAfterSignup hook. ✅
  • we should investigate how to have "state" when using the onBeforeOAuthRedirect hook. This is the only OAuth hook where the developer can get some user input from the client. This input gets lost in the follow up hooks e.g. onAfterSignup doesn't have access to it, so it never can be saved on the user entity.
    • Idea: It would be ideal to somehow keep track of the user input across the OAuth hooks so the it can be used again in the onAfterSignup hook ✅
    • I'll investigate how to best achieve this.

@@ -77,13 +77,11 @@ const _waspConfig: ProviderConfig = {

return createOAuthProviderRouter({
provider,
stateTypes: ['state'],
optionalStateTypes: [],
Copy link
Contributor Author

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();
Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add hooks after login / sign-in, to execute custom server code
1 participant