interim
This commit is contained in:
@@ -77,11 +77,55 @@ API_ROOT = "http://localhost:3000"
|
|||||||
GOOGLE_MAP_API_KEY = AIzaSyCzeVrHt544bp_72YCFkw21eDsmI2JEsQo
|
GOOGLE_MAP_API_KEY = AIzaSyCzeVrHt544bp_72YCFkw21eDsmI2JEsQo
|
||||||
|
|
||||||
|
|
||||||
EMAIL_SERVER_HOST=smtp.livemail.co.uk
|
//EMAIL_SERVER_HOST=smtp.livemail.co.uk
|
||||||
|
//EMAIL_SERVER_PORT=465
|
||||||
|
//EMAIL_SERVER_USER=rob@rdbsolutions.co.uk
|
||||||
|
//EMAIL_SERVER_PASSWORD=R0b3rt72
|
||||||
|
//EMAIL_FROM=rob@rdbsolutions.co.uk
|
||||||
|
|
||||||
|
EMAIL_SERVER_HOST=smtp.sendgrid.net
|
||||||
EMAIL_SERVER_PORT=465
|
EMAIL_SERVER_PORT=465
|
||||||
EMAIL_SERVER_USER=rob@rdbsolutions.co.uk
|
EMAIL_SERVER_USER=apikey
|
||||||
EMAIL_SERVER_PASSWORD=R0b3rt72
|
EMAIL_SERVER_PASSWORD=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
|
||||||
EMAIL_FROM=no-reply@example.com
|
EMAIL_FROM=rob@rdbsolutions.co.uk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NEXT_PUBLIC_MAGIC_PK=pk_live_674E13244D1BB0A4
|
NEXT_PUBLIC_MAGIC_PK=pk_live_674E13244D1BB0A4
|
||||||
MAGIC_SK=sk_live_F0E74A9557051A65
|
MAGIC_SK=sk_live_F0E74A9557051A65
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
NEXTAUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32
|
||||||
|
|
||||||
|
APPLE_ID=
|
||||||
|
APPLE_TEAM_ID=
|
||||||
|
APPLE_PRIVATE_KEY=
|
||||||
|
APPLE_KEY_ID=
|
||||||
|
|
||||||
|
AUTH0_ID=
|
||||||
|
AUTH0_SECRET=
|
||||||
|
AUTH0_ISSUER=
|
||||||
|
|
||||||
|
FACEBOOK_ID=
|
||||||
|
FACEBOOK_SECRET=
|
||||||
|
|
||||||
|
GITHUB_ID=
|
||||||
|
GITHUB_SECRET=
|
||||||
|
|
||||||
|
GOOGLE_ID=
|
||||||
|
GOOGLE_SECRET=
|
||||||
|
|
||||||
|
TWITTER_ID=
|
||||||
|
TWITTER_SECRET=
|
||||||
|
|
||||||
|
//EMAIL_SERVER=smtp://username:password@smtp.example.com:587
|
||||||
|
//EMAIL_FROM=NextAuth <noreply@example.com>
|
||||||
|
|
||||||
|
//DATABASE_URL=sqlite://localhost/:memory:?synchronize=true
|
||||||
|
DATABASE_URL=mysql://nextAuthAdmin:!Sky9fish1972@77.68.17.157:3306/nextauthDB?synchronise=true
|
||||||
|
|
||||||
|
SECRET=SuperSecret
|
||||||
@@ -9,6 +9,8 @@ import { getLogin } from "../../actions";
|
|||||||
import { setLoggedInUserId } from "../../store/accountDetails/action";
|
import { setLoggedInUserId } from "../../store/accountDetails/action";
|
||||||
import { parseCookies, setCookie, destroyCookie } from "nookies";
|
import { parseCookies, setCookie, destroyCookie } from "nookies";
|
||||||
|
|
||||||
|
import { useSession, signIn, signOut } from "next-auth/react";
|
||||||
|
|
||||||
const required = (errorMsg) => (value) =>
|
const required = (errorMsg) => (value) =>
|
||||||
value || typeof value === "number" ? undefined : errorMsg;
|
value || typeof value === "number" ? undefined : errorMsg;
|
||||||
|
|
||||||
@@ -118,6 +120,8 @@ let Login = (props) => {
|
|||||||
GG_REDIRECT_URI,
|
GG_REDIRECT_URI,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const { session, status } = useSession();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { locale } = router;
|
const { locale } = router;
|
||||||
|
|
||||||
@@ -201,12 +205,38 @@ let Login = (props) => {
|
|||||||
router.replace(ggUrl);
|
router.replace(ggUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (session === "loading") {
|
||||||
|
return <h1>loading...</h1>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session) {
|
||||||
|
<>
|
||||||
|
<div>Signed in as {session.user?.email}</div>
|
||||||
|
<br />
|
||||||
|
<button type="button" onClick={() => signOut()}>
|
||||||
|
Signout
|
||||||
|
</button>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="card" id="login-card">
|
<div className="card" id="login-card">
|
||||||
<div className="card-body active">
|
<div className="card-body active">
|
||||||
<h3 className="heading-small card-heading">
|
<h3 className="heading-small card-heading">
|
||||||
{t("home:login-card-title")}
|
{t("home:login-card-title")}
|
||||||
</h3>{" "}
|
</h3>{" "}
|
||||||
|
<div className="govuk-form-group govuk-!-margin-top-4">
|
||||||
|
{" "}
|
||||||
|
Not Signed in
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
className="govuk-button"
|
||||||
|
type="button"
|
||||||
|
onClick={() => signIn()}
|
||||||
|
>
|
||||||
|
Magic Link Sign in
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<form onSubmit={handleSubmit(onHandleSubmit)}>
|
<form onSubmit={handleSubmit(onHandleSubmit)}>
|
||||||
{validLoginID == false && (
|
{validLoginID == false && (
|
||||||
<div className="govuk-error-message govuk-form-group--error">
|
<div className="govuk-error-message govuk-form-group--error">
|
||||||
@@ -298,7 +328,9 @@ let Login = (props) => {
|
|||||||
<a
|
<a
|
||||||
className="govuk-link--no-underline"
|
className="govuk-link--no-underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={t("home:login-comingsoon-forms-link-1")}
|
href={t(
|
||||||
|
"home:login-comingsoon-forms-link-1"
|
||||||
|
)}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{t("home:login-comingsoon-forms-label-1")}
|
{t("home:login-comingsoon-forms-label-1")}
|
||||||
@@ -309,7 +341,9 @@ let Login = (props) => {
|
|||||||
<a
|
<a
|
||||||
className="govuk-link--no-underline"
|
className="govuk-link--no-underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={t("home:login-comingsoon-forms-link-2")}
|
href={t(
|
||||||
|
"home:login-comingsoon-forms-link-2"
|
||||||
|
)}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{t("home:login-comingsoon-forms-label-2")}
|
{t("home:login-comingsoon-forms-label-2")}
|
||||||
@@ -320,7 +354,9 @@ let Login = (props) => {
|
|||||||
<a
|
<a
|
||||||
className="govuk-link--no-underline"
|
className="govuk-link--no-underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={t("home:login-comingsoon-forms-link-3")}
|
href={t(
|
||||||
|
"home:login-comingsoon-forms-link-3"
|
||||||
|
)}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{t("home:login-comingsoon-forms-label-3")}
|
{t("home:login-comingsoon-forms-label-3")}
|
||||||
@@ -331,7 +367,9 @@ let Login = (props) => {
|
|||||||
<a
|
<a
|
||||||
className="govuk-link--no-underline"
|
className="govuk-link--no-underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={t("home:login-comingsoon-forms-link-4")}
|
href={t(
|
||||||
|
"home:login-comingsoon-forms-link-4"
|
||||||
|
)}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{t("home:login-comingsoon-forms-label-4")}
|
{t("home:login-comingsoon-forms-label-4")}
|
||||||
@@ -358,6 +396,7 @@ let Login = (props) => {
|
|||||||
text="Logging in"
|
text="Logging in"
|
||||||
/> */}
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
ValueTransformer,
|
||||||
|
} from "typeorm"
|
||||||
|
|
||||||
|
const transformer = {
|
||||||
|
date: {
|
||||||
|
from: (date ) => date && new Date(parseInt(date, 10)),
|
||||||
|
to: (date) => date?.valueOf().toString(),
|
||||||
|
},
|
||||||
|
bigint: {
|
||||||
|
from: (bigInt) => bigInt && parseInt(bigInt, 10),
|
||||||
|
to: (bigInt) => bigInt?.toString(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ name: "users" })
|
||||||
|
export class UserEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id = undefined;
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
name!
|
||||||
|
|
||||||
|
|
||||||
|
@Column({ type: "varchar", nullable: true, unique: true })
|
||||||
|
email!: string | null
|
||||||
|
|
||||||
|
@Column({ type: "varchar", nullable: true, transformer: transformer.date })
|
||||||
|
emailVerified!: string | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
image!: string | null
|
||||||
|
|
||||||
|
@OneToMany(() => SessionEntity, (session) => session.userId)
|
||||||
|
sessions!: SessionEntity[]
|
||||||
|
|
||||||
|
@OneToMany(() => AccountEntity, (account) => account.userId)
|
||||||
|
accounts!: AccountEntity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ name: "accounts" })
|
||||||
|
export class AccountEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id!: string
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
userId!: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
type!: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
provider!: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
providerAccountId!: string
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
refresh_token!: string | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
access_token!: string | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true,
|
||||||
|
type: "bigint",
|
||||||
|
transformer: transformer.bigint,
|
||||||
|
})
|
||||||
|
expires_at!: number | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
token_type!: string | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
scope!: string | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
id_token!: string | null
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
session_state!: string | null
|
||||||
|
|
||||||
|
@ManyToOne(() => UserEntity, (user) => user.accounts, {
|
||||||
|
createForeignKeyConstraints: true,
|
||||||
|
})
|
||||||
|
user!: UserEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ name: "sessions" })
|
||||||
|
export class SessionEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id!: string
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
sessionToken!: string
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
userId!: string
|
||||||
|
|
||||||
|
@Column({ transformer: transformer.date })
|
||||||
|
expires!: string
|
||||||
|
|
||||||
|
@ManyToOne(() => UserEntity, (user) => user.sessions)
|
||||||
|
user!: UserEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ name: "verification_tokens" })
|
||||||
|
export class VerificationTokenEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id!: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
token!: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
identifier!: string
|
||||||
|
|
||||||
|
@Column({ transformer: transformer.date })
|
||||||
|
expires!: string
|
||||||
|
}
|
||||||
+7
-1
@@ -11,6 +11,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@googlemaps/react-wrapper": "^1.1.24",
|
"@googlemaps/react-wrapper": "^1.1.24",
|
||||||
"@magic-sdk/admin": "^1.4.0",
|
"@magic-sdk/admin": "^1.4.0",
|
||||||
|
"@next-auth/prisma-adapter": "^1.0.3",
|
||||||
|
"@next-auth/typeorm-legacy-adapter": "^1.0.3",
|
||||||
|
"@prisma/client": "3.3.0",
|
||||||
"@webdeb/next-styles": "^1.1.1",
|
"@webdeb/next-styles": "^1.1.1",
|
||||||
"applicationinsights": "^2.2.0",
|
"applicationinsights": "^2.2.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
@@ -35,6 +38,7 @@
|
|||||||
"magic-sdk": "^8.1.0",
|
"magic-sdk": "^8.1.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"multer": "^1.4.4",
|
"multer": "^1.4.4",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
"nanoid": "^3.2.0",
|
"nanoid": "^3.2.0",
|
||||||
"next": "^12.0.8",
|
"next": "^12.0.8",
|
||||||
"next-auth": "^4.3.1",
|
"next-auth": "^4.3.1",
|
||||||
@@ -65,6 +69,7 @@
|
|||||||
"sass": "1.32",
|
"sass": "1.32",
|
||||||
"sass-loader": "^12.4.0",
|
"sass-loader": "^12.4.0",
|
||||||
"swr": "^1.1.2",
|
"swr": "^1.1.2",
|
||||||
|
"typeorm": "^0.3.5",
|
||||||
"webpack": "^5.66.0",
|
"webpack": "^5.66.0",
|
||||||
"xml2js": "^0.4.23",
|
"xml2js": "^0.4.23",
|
||||||
"xmldom": "^0.6.0",
|
"xmldom": "^0.6.0",
|
||||||
@@ -74,6 +79,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.6.0",
|
"eslint": "^8.6.0",
|
||||||
"eslint-config-next": "^12.0.8",
|
"eslint-config-next": "^12.0.8",
|
||||||
"prettier": "2.5.1"
|
"prettier": "2.5.1",
|
||||||
|
"prisma": "3.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
import Providers from "next-auth/providers";
|
import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter";
|
||||||
import { Magic } from "@magic-sdk/admin";
|
import EmailProvider from "next-auth/providers/email";
|
||||||
|
import * as entities from "../../../lib/entities";
|
||||||
const magic = new Magic(process.env.MAGIC_SK);
|
|
||||||
|
|
||||||
|
// For more information on each option (and a full list of options) go to
|
||||||
|
// https://next-auth.js.org/configuration/options
|
||||||
export default NextAuth({
|
export default NextAuth({
|
||||||
session: {
|
// https://next-auth.js.org/configuration/providers
|
||||||
jwt: true,
|
|
||||||
},
|
|
||||||
pages: {
|
|
||||||
// override signIn page so we can integrate with Magic
|
|
||||||
signIn: "/auth/signin",
|
|
||||||
},
|
|
||||||
providers: [
|
providers: [
|
||||||
Providers.Credentials({
|
EmailProvider({
|
||||||
name: "Magic Link",
|
server: {
|
||||||
credentials: {
|
host: process.env.EMAIL_SERVER_HOST,
|
||||||
didToken: { label: "DID Token", type: "text" },
|
port: process.env.EMAIL_SERVER_PORT,
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL_SERVER_USER,
|
||||||
|
pass: process.env.EMAIL_SERVER_PASSWORD,
|
||||||
},
|
},
|
||||||
async authorize({ didToken }, req) {
|
|
||||||
// validate magic DID token
|
|
||||||
magic.token.validate(didToken);
|
|
||||||
|
|
||||||
// fetch user metadata
|
|
||||||
const metadata = await magic.users.getMetadataByToken(didToken);
|
|
||||||
|
|
||||||
// return user info
|
|
||||||
return { ...metadata };
|
|
||||||
},
|
},
|
||||||
|
from: process.env.EMAIL_FROM,
|
||||||
|
maxAge: 10 * 60, // Magic links are valid for 10 min only
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
adapter: TypeORMLegacyAdapter({
|
||||||
|
type: "mysql",
|
||||||
|
database:
|
||||||
|
"mysql://nextAuthAdmin:!Sky9fish1972@77.68.17.157:3306/nextauthDB?synchronise=true",
|
||||||
|
synchronize: false,
|
||||||
|
}),
|
||||||
|
secret: "SuperSecret",
|
||||||
|
session: {
|
||||||
|
jwt: true,
|
||||||
|
pages: {},
|
||||||
|
theme: "dark",
|
||||||
|
debug: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import { signIn } from "next-auth/client";
|
|
||||||
|
|
||||||
// magic-sdk is only availabile in the browser
|
|
||||||
const magic =
|
|
||||||
typeof window !== "undefined" &&
|
|
||||||
new Magic(process.env.NEXT_PUBLIC_MAGIC_PK || "a");
|
|
||||||
|
|
||||||
export default function SignIn() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { register, handleSubmit } = useForm();
|
|
||||||
|
|
||||||
const onSubmit = async ({ email }) => {
|
|
||||||
if (!magic) throw new Error(`magic not defined`);
|
|
||||||
|
|
||||||
// login with Magic
|
|
||||||
const didToken = await magic.auth.loginWithMagicLink({ email });
|
|
||||||
|
|
||||||
// sign in with NextAuth
|
|
||||||
await signIn("credentials", {
|
|
||||||
didToken,
|
|
||||||
callbackUrl: router.query["callbackUrl"],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<input
|
|
||||||
{...register("email", { required: true })}
|
|
||||||
placeholder="nick@example.com"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button type="submit">Sign in</button>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
model Account {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
type String
|
||||||
|
provider String
|
||||||
|
providerAccountId String
|
||||||
|
refresh_token String? @db.Text
|
||||||
|
access_token String? @db.Text
|
||||||
|
expires_at Int?
|
||||||
|
token_type String?
|
||||||
|
scope String?
|
||||||
|
id_token String? @db.Text
|
||||||
|
session_state String?
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([provider, providerAccountId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Session {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
sessionToken String @unique
|
||||||
|
userId String
|
||||||
|
expires DateTime
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String?
|
||||||
|
email String? @unique
|
||||||
|
emailVerified DateTime?
|
||||||
|
image String?
|
||||||
|
accounts Account[]
|
||||||
|
sessions Session[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model VerificationToken {
|
||||||
|
identifier String
|
||||||
|
token String @unique
|
||||||
|
expires DateTime
|
||||||
|
|
||||||
|
@@unique([identifier, token])
|
||||||
|
}
|
||||||
+3818
-2371
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user