Skip to content

Commit

Permalink
Merge pull request #3 from camer0nluo/login
Browse files Browse the repository at this point in the history
feature: add login and forgot password page
  • Loading branch information
camer0nluo authored Dec 6, 2023
2 parents e32a6ee + b65e32b commit 4c4b75e
Show file tree
Hide file tree
Showing 16 changed files with 303 additions and 92 deletions.
6 changes: 6 additions & 0 deletions api/db/supabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(
`${process.env.SUPABASE_URL}`,
`${process.env.SUPABASE_KEY}`
)
24 changes: 13 additions & 11 deletions api/src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Decoded } from '@redwoodjs/api'
import { parseJWT } from '@redwoodjs/api'
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'

/**
Expand All @@ -12,6 +12,11 @@ type RedwoodUser = Record<string, unknown> & { roles?: string[] }
* an optional collection of roles used by requireAuth() to check
* if the user is authenticated or has role-based access
*
* @param decoded - The decoded access token containing user info and JWT claims like `sub`. Note could be null.
* @param { token, SupportedAuthTypes type } - The access token itself as well as the auth provider type
* @param { APIGatewayEvent event, Context context } - An object which contains information from the invoker
* such as headers and cookies, and the context information about the invocation such as IP Address
*
* !! BEWARE !! Anything returned from this function will be available to the
* client--it becomes the content of `currentUser` on the web side (as well as
* `context.currentUser` on the api side). You should carefully add additional
Expand All @@ -20,23 +25,20 @@ type RedwoodUser = Record<string, unknown> & { roles?: string[] }
*
* @see https://github.com/redwoodjs/redwood/tree/main/packages/auth for examples
*
* @param decoded - The decoded access token containing user info and JWT
* claims like `sub`. Note, this could be null.
* @param { token, SupportedAuthTypes type } - The access token itself as well
* as the auth provider type
* @param { APIGatewayEvent event, Context context } - An optional object which
* contains information from the invoker such as headers and cookies, and the
* context information about the invocation such as IP Address
* @returns RedwoodUser
*/
export const getCurrentUser = async (
decoded: Decoded
): Promise<RedwoodUser | null> => {
decoded,
/* eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars */
{ token, type },
/* eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars */
{ event, context }
): Promise<RedwoodUser> => {
if (!decoded) {
return null
}

const roles = decoded[process.env.AUTH0_AUDIENCE + '/roles']
const { roles } = parseJWT({ decoded })

if (roles) {
return { ...decoded, roles }
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
]
},
"devDependencies": {
"@redwoodjs/auth-auth0-setup": "6.4.1",
"@redwoodjs/auth-supabase-setup": "6.4.1",
"@redwoodjs/cli-storybook": "6.4.1",
"@redwoodjs/core": "6.4.1",
"prettier-plugin-tailwindcss": "0.4.1"
Expand All @@ -25,7 +25,9 @@
},
"packageManager": "[email protected]",
"dependencies": {
"@auth0/auth0-react": "^2.2.3",
"@supabase/auth-ui-react": "^0.4.6",
"@supabase/auth-ui-shared": "^0.1.8",
"@supabase/supabase-js": "^2.39.0",
"dotenv": "^16.3.1"
}
}
2 changes: 2 additions & 0 deletions redwood.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"AUTH0_CLIENT_ID",
"AUTH0_REDIRECT_URI",
"AUTH0_AUDIENCE",
"SUPABASE_URL",
"SUPABASE_KEY",
]
[api]
port = 8911
Expand Down
4 changes: 2 additions & 2 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import './index.css'

import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'

const App = () => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
Expand Down
27 changes: 18 additions & 9 deletions web/src/Routes.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { PrivateSet, Route, Router, Set } from '@redwoodjs/router'

import AuthLayout from './layouts/AuthLayout/AuthLayout'
import InteriorLayout from './layouts/InteriorLayout/InteriorLayout'

// In this file, all Page components from 'src/pages` are auto-imported. Nested
// directories are supported, and should be uppercase. Each subdirectory will be
// prepended onto the component name.
Expand All @@ -6,18 +11,22 @@
//
// 'src/pages/HomePage/HomePage.js' -> HomePage
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage

import { Route, Router } from '@redwoodjs/router'

const Routes = () => {
return (
<Router>
<Route path="/signup" page={SignupPage} name="signup" />
<Route path="/forgotpassword" page={ForgotPasswordPage} name="forgotpassword" />
<Route path="/logout" page={LogoutPage} name="logout" />
<Route path="/login" page={LoginPage} name="login" />
<Route path="/" page={HomePage} name="home" />
<Route notfound page={NotFoundPage} />
<PrivateSet unauthenticated="login">
<Set wrap={InteriorLayout}>
<Route path="/dashboard" page={DashboardPage} name="dashboard" />
</Set>
</PrivateSet>
<Set wrap={AuthLayout}>
<Route path="/signup" page={SignupPage} name="signup" />
<Route path="/forgotpassword" page={ForgotPasswordPage} name="forgotpassword" />
<Route path="/logout" page={LogoutPage} name="logout" />
<Route path="/login" page={LoginPage} name="login" />
<Route path="/" page={HomePage} name="home" />
<Route notfound page={NotFoundPage} />
</Set>
</Router>
)
}
Expand Down
13 changes: 13 additions & 0 deletions web/src/pages/DashboardPage/DashboardPage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Meta, StoryObj } from '@storybook/react'

import DashboardPage from './DashboardPage'

const meta: Meta<typeof DashboardPage> = {
component: DashboardPage,
}

export default meta

type Story = StoryObj<typeof DashboardPage>

export const Primary: Story = {}
14 changes: 14 additions & 0 deletions web/src/pages/DashboardPage/DashboardPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { render } from '@redwoodjs/testing/web'

import DashboardPage from './DashboardPage'

// Improve this test with help from the Redwood Testing Doc:
// https://redwoodjs.com/docs/testing#testing-pages-layouts

describe('DashboardPage', () => {
it('renders successfully', () => {
expect(() => {
render(<DashboardPage />)
}).not.toThrow()
})
})
21 changes: 21 additions & 0 deletions web/src/pages/DashboardPage/DashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'

const DashboardPage = () => {
return (
<>
<MetaTags title="Dashboard" description="Dashboard page" />

<h1>DashboardPage</h1>
<p>
Find me in <code>./web/src/pages/DashboardPage/DashboardPage.tsx</code>
</p>
<p>
My default route is named <code>dashboard</code>, link to me with `
<Link to={routes.dashboard()}>Dashboard</Link>`
</p>
</>
)
}

export default DashboardPage
6 changes: 3 additions & 3 deletions web/src/pages/ForgotPasswordPage/ForgotPasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import ShowHidePassword from 'src/components/ShowHidePassword/ShowHidePassword'
const ForgotPasswordPage = () => {
return (
<>
<MetaTags title="Login" description="Login page" />
<div className="container mx-auto">
<MetaTags title="Forgot Password" description="forgot password page" />
<div className="container mx-auto ">
<HeaderWithRulers heading="Forgot Password?" className="text-white" />
<Form>
<ShowHidePassword name="old password" />
<ShowHidePassword name="new password" />
<ShowHidePassword name="confirm password" />
<Button
handleClick={() => {
console.log('clicked')
Expand Down
18 changes: 2 additions & 16 deletions web/src/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
import { Auth0Provider } from '@auth0/auth0-react'

import { MetaTags } from '@redwoodjs/web'

import AuthLayout from 'src/layouts/AuthLayout/AuthLayout'

import LoginPage from '../LoginPage/LoginPage'

const HomePage = () => {
return (
<>
<AuthLayout>
<Auth0Provider
domain={process.env.AUTH0_DOMAIN}
clientId={process.env.AUTH0_CLIENT_ID}
authorizationParams={{
redirect_uri: window.location.origin,
}}
>
<MetaTags title="Home" description="Home Page" />
<LoginPage></LoginPage>
</Auth0Provider>
</AuthLayout>
<MetaTags title="Home" description="Home Page" />
<LoginPage></LoginPage>
</>
)
}
Expand Down
2 changes: 0 additions & 2 deletions web/src/pages/HomePage/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { useAuth0 } from '@auth0/auth0-react'

import Button from 'src/components/Button'

const LoginButton = () => {
Expand Down
4 changes: 0 additions & 4 deletions web/src/pages/HomePage/Logout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React from 'react'

import { useAuth0 } from '@auth0/auth0-react'

import Button from 'src/components/Button'

const LogoutButton = () => {
const { logout } = useAuth0()

return (
<Button
handleClick={() =>
Expand Down
33 changes: 27 additions & 6 deletions web/src/pages/LoginPage/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { supabase } from 'api/db/supabase'

import { Form } from '@redwoodjs/forms'
import { Link, routes } from '@redwoodjs/router'
import { Link, navigate, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'

import Button from 'src/components/Button/Button'
Expand All @@ -8,14 +10,29 @@ import Input from 'src/components/Input/Input'
import ShowHidePassword from 'src/components/ShowHidePassword/ShowHidePassword'

const LoginPage = () => {
const onSubmit = (inputs) => {
signIn(inputs)
onLogin()
}
const onLogin = () => {
navigate(routes.dashboard())
}
async function signIn(state) {
console.log(state)
const { data, error } = await supabase.auth.signInWithPassword({
email: state.Email,
password: state.Password,
})
console.log(data.user.role)
}
return (
<>
<MetaTags title="Login" description="Login page" />
<div className="container mx-auto items-center justify-center">
<div className="container mx-auto items-center justify-center">
<HeaderWithRulers heading="Login" className="text-white" />
<Form>
<Input name="Username" />
<ShowHidePassword name="password" />
<Form onSubmit={onSubmit}>
<Input name="Email" />
<ShowHidePassword name="Password" />
<Button
handleClick={() => {
console.log('clicked')
Expand All @@ -26,10 +43,14 @@ const LoginPage = () => {
>
Submit
</Button>
<div className="items-center justify-center ">
<div className="container mx-auto items-center justify-center ">
<Link to={routes.signup()} className="underline">
Need an account?
</Link>
<br />
<Link to={routes.forgotpassword()} className="underline">
Forgot Password?
</Link>
</div>
</Form>
</div>
Expand Down
60 changes: 50 additions & 10 deletions web/src/pages/SignupPage/SignupPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,59 @@
import { supabase } from 'api/db/supabase'

import { Form } from '@redwoodjs/forms'
import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'
import { Toaster } from '@redwoodjs/web/toast'

import Button from 'src/components/Button/Button'
import HeaderWithRulers from 'src/components/HeaderWithRulers/HeaderWithRulers'
import Input from 'src/components/Input/Input'
import ShowHidePassword from 'src/components/ShowHidePassword/ShowHidePassword'

const SignupPage = () => {
const [toastMessage, setToastMessage] = React.useState(false)
const onSubmit = (inputs) => {
signUpNewUser(inputs)
}

async function signUpNewUser(state) {
console.log(state)
const { data, error } = await supabase.auth.signUp({
email: state.Email,
password: state.Password,
options: {
data: {
name: state.Name,
},
},
})
if (error == null) {
setToastMessage(true)
}
}

return (
<>
<MetaTags title="Signup" description="Signup page" />

<h1>SignupPage</h1>
<p>
Find me in <code>./web/src/pages/SignupPage/SignupPage.tsx</code>
</p>
<p>
My default route is named <code>signup</code>, link to me with `
<Link to={routes.signup()}>Signup</Link>`
</p>
<MetaTags title="Signup" description="sign up page" />
<Toaster />
<div className="container mx-auto">
<HeaderWithRulers heading="Sign Up" className="text-white" />
<Form onSubmit={onSubmit}>
<Input name="Name" />
<Input name="Email" />
<ShowHidePassword name="Password" />
<Button
handleClick={() => {
console.log('hi')
}}
className="
w-full
rounded-full bg-supernova py-5 font-handwriting text-3xl uppercase text-black"
>
Submit
</Button>
</Form>
</div>
</>
)
}
Expand Down
Loading

0 comments on commit 4c4b75e

Please sign in to comment.