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

SDK fixes for Email and Social auth #1654

Merged
merged 2 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import React, { useEffect, useRef } from 'react'
import { useHistory } from 'react-router-dom'

import config from '../../config.js'
import api from '../../api'
import { initSession } from '../helpers/user'
import config from 'wasp/core/config'
import api from 'wasp/api'
import { initSession } from 'wasp/auth/helpers/user'

// After a user authenticates via an Oauth 2.0 provider, this is the page that
// the provider should redirect them to, while providing query string parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { styled } from 'wasp/stitches.config'
import { styled } from 'wasp/core/stitches.config'

export const SocialButton = styled('a', {
display: 'flex',
Expand Down
20 changes: 16 additions & 4 deletions waspc/data/Generator/templates/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,30 @@
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/user": "./dist/auth/user.js",
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/helpers/user": "./dist/auth/helpers/user.js",
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/session": "./dist/auth/session.js",
{=! Not sure who uses this, ask Miho. Our code definitely does, I don't know about the users =}
{=! Used by user, documented. =}
"./auth/providers/types": "./dist/auth/providers/types.js",
{=! Used by user, documented. =}
"./auth/utils": "./dist/auth/utils.js",
{=! Not sure who uses this, ask Miho. Our code definitely does, I don't know about the users =}
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/password": "./dist/auth/password.js",
{=! Not sure who uses this, ask Miho. Our code definitely does, I don't know about the users =}
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/jwt": "./dist/auth/jwt.js",
{=! Used by user, documented. =}
"./auth/validation": "./dist/auth/validation.js",
{=! Used by users, documented. =}
"./auth/forms/Login": "./dist/auth/forms/Login.jsx",
{=! Used by users, documented. =}
"./auth/forms/Signup": "./dist/auth/forms/Signup.jsx",
{=! Not sure who uses this, ask Miho. Our code definitely does, I don't know about the users =}
{=! Used by users, documented. =}
"./auth/forms/VerifyEmail": "./dist/auth/forms/VerifyEmail.jsx",
{=! Used by users, documented. =}
"./auth/forms/ForgotPassword": "./dist/auth/forms/ForgotPassword.jsx",
{=! Used by users, documented. =}
"./auth/forms/ResetPassword": "./dist/auth/forms/ResetPassword.jsx",
{=! Used by our code, uncodumented (but accessible) for users. =}
"./auth/pages/createAuthRequiredPage": "./dist/auth/pages/createAuthRequiredPage.jsx",
{=! Used by users, documented. =}
"./api": "./dist/api/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
findAuthIdentity,
deserializeAndSanitizeProviderData,
type EmailProviderData,
} from 'wasp/server/utils';
} from 'wasp/auth/utils';
import waspServerConfig from 'wasp/server/config';
import { type {= userEntityUpper =}, type {= authEntityUpper =} } from 'wasp/entities'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import {
validateAndGetUserFields,
} from 'wasp/auth/utils'
import { createSession } from "wasp/auth/session"
import { type {= authEntityUpper =} } from "wasp/auth/entities"
import { type {= authEntityUpper =} } from "wasp/entities"
import type { ProviderConfig, RequestWithWasp, UserSignupFields } from "wasp/auth/providers/types"
import { handleRejection } from "wasp/auth/utils"
import { handleRejection } from "wasp/server/utils"

// For oauth providers, we have an endpoint /login to get the auth URL,
// and the /callback endpoint which is used to get the actual access_token and the user info.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import {
} from './types'
import { LoginSignupForm } from './internal/common/LoginSignupForm'
import { MessageError, MessageSuccess } from './internal/Message'
import { ForgotPasswordForm } from './internal/email/ForgotPasswordForm'
import { ResetPasswordForm } from './internal/email/ResetPasswordForm'
import { VerifyEmailForm } from './internal/email/VerifyEmailForm'

const logoStyle = {
height: '3rem'
Expand Down Expand Up @@ -51,6 +54,9 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal', additiona
const titles: Record<State, string> = {
login: 'Log in to your account',
signup: 'Create a new account',
"forgot-password": "Forgot your password?",
"reset-password": "Reset your password",
"verify-email": "Email verification",
}
const title = titles[state]

Expand All @@ -77,6 +83,9 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal', additiona
additionalSignupFields={additionalSignupFields}
/>
)}
{state === 'forgot-password' && (<ForgotPasswordForm />)}
{state === 'reset-password' && (<ResetPasswordForm />)}
{state === 'verify-email' && (<VerifyEmailForm />)}
</AuthContext.Provider>
</Container>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,80 @@ import type {
AdditionalSignupFieldRenderFn,
FormState,
} from '../../types'
import * as SocialIcons from '../social/SocialIcons'
import { SocialButton } from '../social/SocialButton'
import { useHistory } from 'react-router-dom'
import { useUsernameAndPassword } from '../usernameAndPassword/useUsernameAndPassword'
import { useEmail } from '../email/useEmail'

const OrContinueWith = styled('div', {
position: 'relative',
marginTop: '1.5rem'
})

const OrContinueWithLineContainer = styled('div', {
position: 'absolute',
inset: '0px',
display: 'flex',
alignItems: 'center'
})

const OrContinueWithLine = styled('div', {
width: '100%',
borderTopWidth: '1px',
borderColor: '$gray500'
})

const OrContinueWithTextContainer = styled('div', {
position: 'relative',
display: 'flex',
justifyContent: 'center',
fontSize: '$sm'
})

const OrContinueWithText = styled('span', {
backgroundColor: 'white',
paddingLeft: '0.5rem',
paddingRight: '0.5rem'
})
const SocialAuth = styled('div', {
marginTop: '1.5rem'
})

const SocialAuthLabel = styled('div', {
fontWeight: '500',
fontSize: '$sm'
})

const SocialAuthButtons = styled('div', {
marginTop: '0.5rem',
display: 'flex',

variants: {
direction: {
horizontal: {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(48px, 1fr))',
},
vertical: {
flexDirection: 'column',
margin: '8px 0',
}
},
gap: {
small: {
gap: '4px',
},
medium: {
gap: '8px',
},
large: {
gap: '16px',
}
}
}
})
const googleSignInUrl = `${config.apiUrl}/auth/google/login`
const gitHubSignInUrl = `${config.apiUrl}/auth/github/login`

export type LoginSignupFormFields = {
[key: string]: string;
Expand Down Expand Up @@ -50,10 +121,14 @@ export const LoginSignupForm = ({
};
const hookForm = useForm<LoginSignupFormFields>()
const { register, formState: { errors }, handleSubmit: hookFormHandleSubmit } = hookForm
const { handleSubmit } = useUsernameAndPassword({
const { handleSubmit } = useEmail({
isLogin,
onError: onErrorHandler,
onSuccess() {
showEmailVerificationPending() {
hookForm.reset()
setSuccessMessage(`You've signed up successfully! Check your email for the confirmation link.`)
},
onLoginSuccess() {
history.push('/')
},
});
Expand All @@ -69,17 +144,33 @@ export const LoginSignupForm = ({
}

return (<>
<SocialAuth>
<SocialAuthLabel>{cta} with</SocialAuthLabel>
<SocialAuthButtons gap='large' direction={socialButtonsDirection}>
<SocialButton href={googleSignInUrl}><SocialIcons.Google/></SocialButton>

<SocialButton href={gitHubSignInUrl}><SocialIcons.GitHub/></SocialButton>
</SocialAuthButtons>
</SocialAuth>
<OrContinueWith>
<OrContinueWithLineContainer>
<OrContinueWithLine/>
</OrContinueWithLineContainer>
<OrContinueWithTextContainer>
<OrContinueWithText>Or continue with</OrContinueWithText>
</OrContinueWithTextContainer>
</OrContinueWith>
<Form onSubmit={hookFormHandleSubmit(onSubmit)}>
<FormItemGroup>
<FormLabel>Username</FormLabel>
<FormLabel>E-mail</FormLabel>
<FormInput
{...register('username', {
required: 'Username is required',
{...register('email', {
required: 'Email is required',
})}
type="text"
type="email"
disabled={isLoading}
/>
{errors.username && <FormError>{errors.username.message}</FormError>}
{errors.email && <FormError>{errors.email.message}</FormError>}
</FormItemGroup>
<FormItemGroup>
<FormLabel>Password</FormLabel>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import type { LoginSignupFormFields } from './internal/common/LoginSignupForm'
export enum State {
Login = 'login',
Signup = 'signup',
ForgotPassword = 'forgot-password',
ResetPassword = 'reset-password',
VerifyEmail = 'verify-email',
}

export type CustomizationOptions = {
Expand Down
13 changes: 0 additions & 13 deletions waspc/examples/todo-typescript/.wasp/out/sdk/wasp/auth/login.ts

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@
"./auth/logout": "./dist/auth/logout.js",
"./auth/useAuth": "./dist/auth/useAuth.js",
"./auth/user": "./dist/auth/user.js",
"./auth/helpers/user": "./dist/auth/helpers/user.js",
"./auth/session": "./dist/auth/session.js",
"./auth/providers/types": "./dist/auth/providers/types.js",
"./auth/utils": "./dist/auth/utils.js",
"./auth/password": "./dist/auth/password.js",
"./auth/jwt": "./dist/auth/jwt.js",
"./auth/validation": "./dist/auth/validation.js",
"./auth/forms/Login": "./dist/auth/forms/Login.jsx",
"./auth/forms/Signup": "./dist/auth/forms/Signup.jsx",
"./auth/forms/VerifyEmail": "./dist/auth/forms/VerifyEmail.jsx",
"./auth/forms/ForgotPassword": "./dist/auth/forms/ForgotPassword.jsx",
"./auth/forms/ResetPassword": "./dist/auth/forms/ResetPassword.jsx",
"./auth/pages/createAuthRequiredPage": "./dist/auth/pages/createAuthRequiredPage.jsx",
"./api": "./dist/api/index.js",
"./api/*": "./dist/api/*",
Expand Down
47 changes: 41 additions & 6 deletions waspc/examples/todo-typescript/main.wasp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,30 @@ app TodoTypescript {
auth: {
userEntity: User,
methods: {
usernameAndPassword: {}, // this is a very naive implementation, use 'email' in production instead
//google: {}, //https://wasp-lang.dev/docs/integrations/google
//gitHub: {}, //https://wasp-lang.dev/docs/integrations/github
//email: {} //https://wasp-lang.dev/docs/guides/email-auth
// usernameAndPassword: {}, // this is a very naive implementation, use 'email' in production instead
google: {
userSignupFields: import { googleUserSignupFields } from "@src/user/userSignupFields.js"
},
gitHub: {
userSignupFields: import { githubUserSignupFields } from "@src/user/userSignupFields.js"
},
email: {
userSignupFields: import { userSignupFields } from "@src/user/userSignupFields.js",
fromField: {
email: "[email protected]"
},
emailVerification: {
clientRoute: EmailVerificationRoute,
},
passwordReset: {
clientRoute: PasswordResetRoute,
}
} // https://wasp-lang.dev/docs/guides/email-auth
},
onAuthFailedRedirectTo: "/login",
},
emailSender: {
provider: Dummy
}
}

Expand All @@ -21,6 +39,7 @@ app TodoTypescript {
// Then run `wasp db studio` to open Prisma Studio and view your db models
entity User {=psl
id Int @id @default(autoincrement())
address String
tasks Task[]
psl=}

Expand All @@ -41,12 +60,28 @@ page MainPage {

route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
component: import { LoginPage } from "@src/user/LoginPage.tsx"
component: import { LoginPage } from "@src/user/auth.tsx"
}

route SignupRoute { path: "/signup", to: SignupPage }
page SignupPage {
component: import { SignupPage } from "@src/user/SignupPage.tsx"
component: import { SignupPage } from "@src/user/auth.tsx"
}

route EmailVerificationRoute { path: "/email-verify", to: EmailVerification }
page EmailVerification {
component: import { EmailVerificationPage } from "@src/user/auth.tsx"
}

route RequestPasswordResetRoute { path: "/request-password-reset", to: RequestPasswordReset }
page RequestPasswordReset {
component: import { RequestPasswordResetPage } from "@src/user/auth.tsx"
}


route PasswordResetRoute { path: "/password-reset", to: PasswordReset }
page PasswordReset {
component: import { PasswordResetPage } from "@src/user/auth.tsx"
}

query getTasks {
Expand Down
Loading
Loading