WIP Custom auth handler / login

Forgot password is not working
This commit is contained in:
Michael
2021-08-05 21:12:12 +02:00
parent 56bc4f24a9
commit 67631d2ba8
11 changed files with 283 additions and 991 deletions

View File

@@ -6,18 +6,18 @@ CONFIGURE THE AUTH COMPONENT LINE 30
You can select your auth providers, or just keep the email/password. You can
check the providers available here: https://supabase.io/docs/guides/auth
*/
import { Auth } from "@supabase/ui";
import Login from "./UI/Login";
import { supabase } from "utils/supabaseClient";
import { useAuth } from "utils/Authcontext";
const Container = (props) => {
const { user } = Auth.useUser();
const { user, signOut } = useAuth();
if (user)
return (
<div className='w-80 md:w-96 order-first lg:order-last'>
<p>Hello {user.email}! 👋 You are already logged in</p>
<button
className='btn btn-primary'
onClick={() => props.supabaseClient.auth.signOut()}>
<button className='btn btn-primary' onClick={() => signOut()}>
Sign out
</button>
</div>
@@ -26,19 +26,16 @@ const Container = (props) => {
};
const AuthComponent = () => {
const { signUp, signIn, signOut, resetPassword } = useAuth();
return (
<Auth.UserContextProvider supabaseClient={supabase}>
<Container supabaseClient={supabase}>
<Auth
supabaseClient={supabase}
providers={["google"]}
socialLayout='horizontal'
socialButtonSize='xlarge'
socialColors={true}
className='p-5 bg-neutral-content rounded-md'
<Login
signUp={signUp}
signIn={signIn}
signOut={signOut}
resetPassword={resetPassword}
/>
</Container>
</Auth.UserContextProvider>
);
};

View File

@@ -12,14 +12,36 @@ The images are in the public folder.
import "react-toastify/dist/ReactToastify.css";
import { Auth } from "@supabase/ui";
import Footer from "./Footer";
import Head from "next/head";
import Nav from "./Nav";
import { ToastContainer } from "react-toastify";
import { supabase } from "utils/supabaseClient";
import { useAuth } from "utils/Authcontext";
import { useEffect } from "react";
const Layout = (props) => {
const { user } = Auth.useUser();
const { user, signOut } = useAuth();
useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
if ((event === "SIGNED_OUT") | (event === "SIGNED_IN")) {
fetch("/api/auth", {
method: "POST",
headers: new Headers({ "Content-Type": "application/json" }),
credentials: "same-origin",
body: JSON.stringify({ event, session }),
}).then((res) => res.json());
}
if (event === "USER_UPDATED") {
}
}
);
return () => {
authListener.unsubscribe();
};
}, []);
const toastStyle = {
//Style your toast elements here
@@ -56,7 +78,7 @@ const Layout = (props) => {
<meta name='theme-color' content='#ffffff' />
</Head>
<div className='max-w-7xl flex flex-col min-h-screen mx-auto p-5'>
<Nav user={user} />
<Nav user={user} signOut={signOut} />
<main className='flex-1'>{props.children}</main>
<ToastContainer
position='bottom-center'

View File

@@ -6,30 +6,8 @@ import Image from "next/image";
import Link from "next/link";
import Logo from "public/logo.svg";
import { Menu } from "react-feather";
import { supabase } from "utils/supabaseClient";
import { useEffect } from "react";
const Nav = (props) => {
useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
if ((event === "SIGNED_OUT") | (event === "SIGNED_IN")) {
fetch("/api/auth", {
method: "POST",
headers: new Headers({ "Content-Type": "application/json" }),
credentials: "same-origin",
body: JSON.stringify({ event, session }),
}).then((res) => res.json());
}
if (event === "USER_UPDATED") {
}
}
);
return () => {
authListener.unsubscribe();
};
}, []);
//Modify you menu directly here
const NavMenu = (
<div className='flex flex-col lg:flex-row lg:space-x-10 lg:m-auto font-body text-sm'>
@@ -50,7 +28,7 @@ const Nav = (props) => {
{props.user ? (
<button
className='btn btn-ghost btn-sm'
onClick={() => supabase.auth.signOut()}>
onClick={() => props.signOut()}>
Logout
</button>
) : (

View File

@@ -9,15 +9,15 @@ https://dashboard.stripe.com/test/settings/billing/portal
import { getSub, supabase } from "utils/supabaseClient";
import { useEffect, useState } from "react";
import { Auth } from "@supabase/ui";
import { Prices } from "utils/priceList";
import { Switch } from "@headlessui/react";
import axios from "axios";
import router from "next/router";
import { useAuth } from "utils/Authcontext";
const Pricing = () => {
const [enabled, setEnabled] = useState(false);
const { user, session } = Auth.useUser();
const { user } = useAuth();
const [customerId, setCustomerId] = useState(null);
const [sub, setSub] = useState(false);
const flat = false; // Switch between subscription system or flat prices
@@ -143,7 +143,8 @@ const Pricing = () => {
<button
className='btn btn-primary w-full'
onClick={
sub
user
? sub
? () => {
portal();
}
@@ -154,8 +155,11 @@ const Pricing = () => {
? Prices.personal.annually.id
: Prices.personal.monthly.id
)
: () => {
router.push("/auth");
}
}>
{sub ? "Upgrade" : "Buy Now"}
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
</button>
</div>
</div>
@@ -192,7 +196,8 @@ const Pricing = () => {
<button
className='btn btn-primary w-full'
onClick={
sub
user
? sub
? () => {
portal();
}
@@ -203,8 +208,11 @@ const Pricing = () => {
? Prices.team.annually.id
: Prices.team.monthly.id
)
: () => {
router.push("/auth");
}
}>
{sub ? "Upgrade" : "Buy Now"}
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
</button>
</div>
</div>
@@ -240,17 +248,23 @@ const Pricing = () => {
<button
className='btn btn-primary w-full'
onClick={
sub
user
? sub
? () => {
portal();
}
: (e) =>
handleSubmit(
e,
enabled ? Prices.pro.annually.id : Prices.pro.monthly.id
enabled
? Prices.pro.annually.id
: Prices.pro.monthly.id
)
: () => {
router.push("/auth");
}
}>
{sub ? "Upgrade" : "Buy Now"}
{user ? (sub ? "Upgrade" : "Buy Now") : "Register"}
</button>
</div>
</div>

133
components/UI/Login.js Normal file
View File

@@ -0,0 +1,133 @@
import { IoLogoGoogle } from "react-icons/io";
import { useState } from "react";
const Login = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [forgot, setForgot] = useState(false);
return (
<div className='p-10 bg-base-100 md:flex-1 rounded-md text-base-content shadow-md'>
{!forgot && (
<>
<h3 className='my-4 text-2xl font-semibold'>Account Login</h3>
<form action='#' className='flex flex-col space-y-5'>
<div className='flex flex-col space-y-1'>
<label
htmlFor='email'
className='text-sm font-semibold text-gray-500'>
Email address
</label>
<input
type='email'
id='email'
autoFocus
className='input input-primary input-bordered input-sm'
value={email}
onChange={(event) => {
setEmail(event.target.value);
}}
/>
</div>
<div className='flex flex-col space-y-1'>
<div className='flex items-center justify-between'>
<label
htmlFor='password'
className='text-sm font-semibold text-gray-500'>
Password
</label>
<button
onClick={() => {
setForgot(true);
}}
className='text-sm text-blue-600 hover:underline focus:text-blue-800'>
Forgot Password?
</button>
</div>
<input
type='password'
id='password'
className='input input-primary input-bordered input-sm'
value={password}
onChange={(event) => {
setPassword(event.target.value);
}}
/>
</div>
<div>
<button
className='btn btn-primary w-full'
onClick={(event) => {
event.preventDefault();
props.signIn({ email: email, password: password });
}}>
Log in
</button>
</div>
<div className='flex flex-col space-y-5'>
<span className='flex items-center justify-center space-x-2'>
<span className='h-px bg-gray-400 w-14'></span>
<span className='font-normal text-gray-500'>or login with</span>
<span className='h-px bg-gray-400 w-14'></span>
</span>
<div className='flex flex-col space-y-4'>
<button
href='#'
className='flex items-center justify-center px-4 py-2 space-x-2 transition-colors duration-300 border border-gray-800 rounded-md group hover:bg-gray-800 focus:outline-none '
onClick={(event) => {
event.preventDefault();
props.signIn({ provider: "google" });
}}>
<div className='text-base-content group-hover:text-white'>
<IoLogoGoogle />
</div>
<span className='text-sm font-medium text-gray-800 group-hover:text-white'>
Gmail
</span>
</button>
</div>
</div>
</form>
</>
)}
{forgot && (
<>
<h3 className='my-4 text-2xl font-semibold'>Password recovery</h3>
<form action='#' className='flex flex-col space-y-5'>
<div className='flex flex-col space-y-1'>
<label
htmlFor='email'
className='text-sm font-semibold text-gray-500'>
Email address
</label>
<input
type='email'
id='email'
autoFocus
className='input input-primary input-bordered input-sm'
value={email}
onChange={(event) => {
setEmail(event.target.value);
}}
/>
</div>
<div>
<button
className='btn btn-primary w-full'
onClick={(event) => {
event.preventDefault();
props.resetPassword(email);
}}>
Recover my password
</button>
</div>
</form>
</>
)}
</div>
);
};
export default Login;

911
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,6 @@
"micro": "^9.3.4",
"next": "latest",
"next-seo": "^4.24.0",
"octokit": "^1.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-feather": "^2.0.9",

View File

@@ -1,8 +1,7 @@
import "./global.css";
import { Auth } from "@supabase/ui";
import { AuthProvider } from "utils/Authcontext";
import { DefaultSeo } from "next-seo";
import { supabase } from "utils/supabaseClient";
/*
Next-seo is integrated by default, if you want more information and how to
@@ -12,7 +11,7 @@ setup more elements, visit their Github page https://github.com/garmeeh/next-seo
function MyApp({ Component, pageProps }) {
return (
<>
<Auth.UserContextProvider supabaseClient={supabase}>
<AuthProvider>
<DefaultSeo
openGraph={{
type: "website",
@@ -26,7 +25,7 @@ function MyApp({ Component, pageProps }) {
}}
/>
<Component {...pageProps} />
</Auth.UserContextProvider>
</AuthProvider>
</>
);
}

View File

@@ -3,8 +3,8 @@
* With SupaNexTail, we use SSR with the Dashboard page (pages/dashboard.js)
*/
import { supabase } from 'utils/supabaseClient'
import { supabase } from "utils/supabaseClient";
export default function handler(req, res) {
supabase.auth.api.setAuthCookie(req, res)
supabase.auth.api.setAuthCookie(req, res);
}

View File

@@ -4,24 +4,12 @@ You have 2 components, the "AuthComponent" that handle the logic,
and the "AuthText" that will show the description on the left of the screen
*/
import { Auth } from "@supabase/ui";
import AuthComponent from "../components/Auth";
import AuthComponent from "components/Auth";
import AuthText from "components/AuthText";
import Layout from "components/Layout";
import { NextSeo } from "next-seo";
import { useEffect } from "react";
import { useRouter } from "next/router";
const AuthPage = () => {
const { user, session } = Auth.useUser();
const router = useRouter();
useEffect(() => {
// If a user is already logged in, return to the homepage
if (user) {
router.push("/");
}
}, [user]);
return (
<>
<NextSeo
@@ -32,7 +20,9 @@ const AuthPage = () => {
<Layout>
<div className='flex flex-wrap justify-evenly w-full'>
<AuthText />
<div>{!session && <AuthComponent />}</div>
<div>
<AuthComponent />
</div>
</div>
</Layout>
</>

33
utils/AuthContext.js Normal file
View File

@@ -0,0 +1,33 @@
import React, { createContext, useContext, useState } from "react";
import { supabase } from "utils/supabaseClient";
// create a context for authentication
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
// create state values for user data and loading
const [user, setUser] = useState();
const [loading, setLoading] = useState(true);
// create signUp, signIn, signOut functions
const value = {
signUp: (data) => supabase.auth.signUp(data),
signIn: (data) => supabase.auth.signIn(data),
signOut: () => supabase.auth.signOut(),
resetPassword: (data) => supabase.auth.api.resetPasswordForEmail(data),
user,
};
// use a provider to pass down the value
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
// export the useAuth hook
export const useAuth = () => {
return useContext(AuthContext);
};