Skip to content

Commit

Permalink
feat: implement sign-in with Email and Magic Link
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP committed Jan 5, 2024
1 parent 6b8a9c0 commit 7654be7
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ dist-ssr

# amplify
.amplify
amplifyconfiguration*
# amplifyconfiguration*
68 changes: 48 additions & 20 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,52 @@
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import "./App.css";
import {
createBrowserRouter,
Outlet,
RouterProvider,
} from "react-router-dom";
import SignUp from "./SignUp.tsx";
import SignIn from "./SignIn.tsx";
import MagicLinkRedirect from "./MagicLinkRedirect.tsx";
import AuthenticatedApp from "./AuthenticatedApp.tsx";
import AuthProvider from "./providers/AuthProvider.tsx";

const router = createBrowserRouter([
{
id: "root",
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <AuthenticatedApp />,
},
{
path: "sign-up",
element: <SignUp />,
},
{
path: "sign-in",
element: <SignIn />,
},
{
path: "sign-in-redirect",
children: [
{
path: ":code",
element: <MagicLinkRedirect />,
},
],
},
],
},
]);

function App() {
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
return <RouterProvider router={router}></RouterProvider>;
}

function Layout() {
return <AuthProvider><Outlet /></AuthProvider>;
}

export default App
export default App;
37 changes: 37 additions & 0 deletions src/AuthenticatedApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getCurrentUser } from "aws-amplify/auth";
import { useContext, useEffect, useState } from "react";
import { Navigate } from "react-router-dom";

import { AuthContext } from "./states/authContext";

function AuthenticatedApp() {
const { session } = useContext(AuthContext);
const [user, setUser] = useState<string>("");

useEffect(() => {
const loadUserName = async () => {
try {
const { username } = await getCurrentUser();
setUser(username);
} catch (error) {
console.log(error);
}
};
loadUserName();
});

return (
<>
{session ? (
<>
<div className="card">
<h1>Authenticated App</h1>
<p>Hello! {user}</p>
</div>
</>
): <Navigate to="/sign-in" />}
</>
);
}

export default AuthenticatedApp;
6 changes: 4 additions & 2 deletions src/MagicLinkRedirect.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { confirmSignIn } from "aws-amplify/auth";

function MagicLinkRedirect() {
const { code } = useParams<"code">();
const navigate = useNavigate();

const handleConfirmSignIn = async () => {
if (code) {
Expand All @@ -11,8 +12,9 @@ function MagicLinkRedirect() {
challengeResponse: code,
});
console.log('❤️❤️❤️', authSession);
navigate("/");
} catch (error) {
console.log(error);
console.error(error);
}
}
};
Expand Down
17 changes: 11 additions & 6 deletions src/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@

import { signIn } from 'aws-amplify/auth';
import { useRef } from 'react';
import { useRef, useState } from 'react';
import './App.css'

function SignIn() {
const usernameFieldRef = useRef<HTMLInputElement>(null);
const emailFieldRef = useRef<HTMLInputElement>(null);
const [showCheckEmailMessage, setShowCheckEmailMessage] = useState<boolean>(false);

const handlePasswordlessLogin = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

const username = usernameFieldRef.current?.value;
const username = emailFieldRef.current?.value;
if (!username) {
return;
}
Expand All @@ -22,6 +23,7 @@ function SignIn() {
method: 'MAGIC_LINK'
}
});
setShowCheckEmailMessage(true);
console.log(authSession);
} catch (error) {
console.log(error);
Expand All @@ -35,16 +37,19 @@ function SignIn() {
<form onSubmit={handlePasswordlessLogin}>
<label>Username:</label>
<input
type="text"
ref={usernameFieldRef}
placeholder="Username"
type='email'
ref={emailFieldRef}
placeholder="email"
required
/>
<br />
<button type="submit">
Sign In With Email and MAGIC_LINK
</button>
</form>
<div className="card" hidden = {!showCheckEmailMessage}>
<p>Check your email for a link to sign in.</p>
</div>
</div>
</>
)
Expand Down
2 changes: 1 addition & 1 deletion src/SignUp.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRef, useState } from 'react';
import { SignUpWithEmailAndMagicLinkInput, SignUpWithEmailAndOTPInput, signUp } from 'aws-amplify/auth';
import { signUp } from 'aws-amplify/auth';

function SignUp() {
const usernameFieldRef = useRef<HTMLInputElement>(null);
Expand Down
26 changes: 26 additions & 0 deletions src/amplifyconfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const amplifyConfig = {
"aws_project_region": "us-east-1",
"aws_user_pools_id": "us-east-1_z2FppwnhK",
"aws_user_pools_web_client_id": "33hdk6q092hql403mglnuhs5c1",
"aws_cognito_region": "us-east-1",
"aws_cognito_identity_pool_id": "us-east-1:68315969-8c96-4c65-a12f-89a9da4cf959",
"aws_cognito_signup_attributes": [
"EMAIL"
],
"aws_cognito_username_attributes": [
"EMAIL"
],
"aws_cognito_verification_mechanisms": [
"EMAIL"
],
"aws_cognito_password_protection_settings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": [
"REQUIRES_NUMBERS",
"REQUIRES_LOWERCASE",
"REQUIRES_UPPERCASE",
"REQUIRES_SYMBOLS"
]
}
}
export default amplifyConfig;
48 changes: 48 additions & 0 deletions src/components/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState, useEffect, useContext } from "react";
import { fetchAuthSession } from "aws-amplify/auth";

import { AuthContext } from "../states/authContext";
import { AuthSession } from "../types";
import { useNavigate } from "react-router-dom";

function AuthProvider({
children
}: {children: React.ReactNode}) {
const { logout } = useContext(AuthContext);
const [authSession, setAuthSession] = useState<AuthSession|null>(null);
const navigate = useNavigate();

useEffect(() => {
fetchAuthSession()
.then((authSession) => {
if (authSession.tokens) {
setAuthSession(authSession);
}
})
.catch((e) => {
console.log(e);
navigate("/sign-in");
});
});

const logoutAndClearSession = async () => {
await logout();
setAuthSession(null);
}

return (
<AuthContext.Provider
value={{
session: authSession,
setSession: setAuthSession,
logout: logoutAndClearSession,
}}
>
<h1>Passwordless Auth Sample App</h1>
{children}
{ authSession && <button onClick={logoutAndClearSession}>Sign Out</button> }
</AuthContext.Provider>
);
}

export default AuthProvider;
30 changes: 1 addition & 29 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import { Amplify, } from 'aws-amplify';
import { Amplify as AmplifyCore } from '@aws-amplify/core';
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import App from './App.tsx'
import './index.css'
import SignUp from './SignUp.tsx';
import SignIn from './SignIn.tsx';
import amplifyconfiguration from './amplifyconfiguration';
import MagicLinkRedirect from './MagicLinkRedirect.tsx';

Amplify.configure(amplifyconfiguration);
AmplifyCore.libraryOptions = {
Expand All @@ -21,30 +17,6 @@ AmplifyCore.libraryOptions = {

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={
createBrowserRouter([
{
path: '/',
element: <App/>,
},
{
path: 'sign-up',
element: <SignUp/>
},
{
path: 'sign-in',
element: <SignIn/>
},
{
path: 'sign-in-redirect',
children: [
{
path: ':code',
element: <MagicLinkRedirect/>
}
]
}
])
}></RouterProvider>
<App />
</React.StrictMode>,
)
13 changes: 13 additions & 0 deletions src/states/authContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createContext } from "react";
import { signOut } from "aws-amplify/auth";
import { AuthSession } from "../types";

export const AuthContext = createContext<{
session: AuthSession | null;
logout: typeof signOut;
setSession: (session: AuthSession|null) => void;
}>({
session: null,
setSession: () => {},
logout: signOut,
});
3 changes: 3 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { fetchAuthSession } from "aws-amplify/auth";

export type AuthSession = Awaited<ReturnType<typeof fetchAuthSession>>;

0 comments on commit 7654be7

Please sign in to comment.