add connections section, unlink, relink
This commit is contained in:
parent
91e54793ff
commit
d246e39211
|
@ -25,5 +25,5 @@ export async function POST(request: Request) {
|
||||||
|
|
||||||
const [_, ticket] = await authTicketLogin(username);
|
const [_, ticket] = await authTicketLogin(username);
|
||||||
// not confident if we can set-cookie and I cba to try
|
// not confident if we can set-cookie and I cba to try
|
||||||
return new Response(JSON.stringify({ ok: true, ticket }));
|
return new Response(JSON.stringify({ ok: true, ticket: ticket.ticket }));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
AuthState,
|
||||||
|
getAuthState,
|
||||||
|
getRegisteringUser,
|
||||||
|
getUser
|
||||||
|
} from "@/auth/auth";
|
||||||
|
import prisma from "@/prisma";
|
||||||
|
|
||||||
|
async function unlinkDiscord(id: string) {
|
||||||
|
await prisma.discordAuth.delete({
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unlinkGitHub(id: string) {
|
||||||
|
await prisma.gitHubAuth.delete({
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUser(id: number) {
|
||||||
|
await prisma.authTicket.deleteMany({
|
||||||
|
where: {
|
||||||
|
userId: id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.user.delete({
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const authState = await getAuthState();
|
||||||
|
|
||||||
|
if (authState == AuthState.Registering) {
|
||||||
|
const registeringUser = (await getRegisteringUser())!;
|
||||||
|
if (registeringUser.discordAuth !== null)
|
||||||
|
await unlinkDiscord(registeringUser.discordAuth.id);
|
||||||
|
|
||||||
|
if (registeringUser.githubAuth !== null)
|
||||||
|
await unlinkGitHub(registeringUser.githubAuth.id);
|
||||||
|
|
||||||
|
await deleteUser(registeringUser.id);
|
||||||
|
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await getUser();
|
||||||
|
if (user == null) return new Response(null, { status: 401 });
|
||||||
|
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const provider = searchParams.get("provider");
|
||||||
|
switch (provider) {
|
||||||
|
case "discord":
|
||||||
|
const discord = await user.getDiscord();
|
||||||
|
if (discord == null) return new Response(null, { status: 400 });
|
||||||
|
await unlinkDiscord(await discord.getId());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "github":
|
||||||
|
const github = await user.getGitHub();
|
||||||
|
if (github == null) return new Response(null, { status: 400 });
|
||||||
|
await unlinkGitHub(await github.getId());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
}
|
|
@ -24,3 +24,49 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* stolen from prettyform */
|
||||||
|
.fancyInput {
|
||||||
|
background: var(--bg-dark);
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0.15rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authProviderList {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: max-content min-content;
|
||||||
|
/* padding */
|
||||||
|
grid-gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authProviderList p {
|
||||||
|
/* flex spam is fun */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the !importants here piss me off but it wouldn't accept the property otherwise */
|
||||||
|
.progress {
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--fg-darker) 50%,
|
||||||
|
var(--bg-dark) 50%
|
||||||
|
) !important;
|
||||||
|
background-size: 200% 100% !important;
|
||||||
|
background-position: right bottom !important;
|
||||||
|
transition: all 0s linear !important;
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0.15rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when clicked */
|
||||||
|
.progress:active {
|
||||||
|
transition: all 3s linear !important;
|
||||||
|
background-position: left bottom !important;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
} from "@/schemas";
|
} from "@/schemas";
|
||||||
import PrettyForm from "@/components/PrettyForm";
|
import PrettyForm from "@/components/PrettyForm";
|
||||||
import Toast from "@/components/Toast";
|
import Toast from "@/components/Toast";
|
||||||
|
import { AuthProviderState } from "@/auth/AuthProvider";
|
||||||
|
import { exec } from "child_process";
|
||||||
|
|
||||||
type UpdateResponse = {
|
type UpdateResponse = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
|
@ -34,7 +36,64 @@ async function fileAsBase64(f: File) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AboutMe({ info }: { info: UserInfo }) {
|
function AuthProviderButton({ provider }: { provider: AuthProviderState }) {
|
||||||
|
// bullshit hack
|
||||||
|
const holdTime = provider.connected ? 3000 : 0;
|
||||||
|
const interval = React.useRef<NodeJS.Timeout | null>();
|
||||||
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const execute = async () => {
|
||||||
|
const name = provider.name.toLowerCase();
|
||||||
|
if (!provider.connected) {
|
||||||
|
window.location.href = `/oauth/${name}/login`;
|
||||||
|
} else {
|
||||||
|
await fetch(`/api/unlink?provider=${name}`, { method: "POST" });
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mouseDown = () => {
|
||||||
|
interval.current = setTimeout(execute, holdTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mouseUp = () => {
|
||||||
|
if (interval.current) clearTimeout(interval.current);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className={
|
||||||
|
styles.fancyInput + " " + (provider.connected ? styles.progress : "")
|
||||||
|
}
|
||||||
|
onMouseDown={mouseDown}
|
||||||
|
onMouseUp={mouseUp}
|
||||||
|
value={provider.connected ? "Disconnect" : "Connect"}
|
||||||
|
ref={inputRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AuthProviderEntry({ provider }: { provider: AuthProviderState }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{provider.name}:{" "}
|
||||||
|
{provider.connected ? provider.username : "Not connected"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<AuthProviderButton provider={provider} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AboutMe({
|
||||||
|
info,
|
||||||
|
providers
|
||||||
|
}: {
|
||||||
|
info: UserInfo;
|
||||||
|
providers: AuthProviderState[];
|
||||||
|
}) {
|
||||||
const [globalError, setGlobalError] = React.useState<string | null>(null);
|
const [globalError, setGlobalError] = React.useState<string | null>(null);
|
||||||
const [madeProfileChanges, setMadeChanges] = React.useState(false);
|
const [madeProfileChanges, setMadeChanges] = React.useState(false);
|
||||||
const [madePasswordChanges, setMadePasswordChanges] = React.useState(false);
|
const [madePasswordChanges, setMadePasswordChanges] = React.useState(false);
|
||||||
|
@ -229,9 +288,10 @@ export default function AboutMe({ info }: { info: UserInfo }) {
|
||||||
|
|
||||||
<hr className={styles.divider} />
|
<hr className={styles.divider} />
|
||||||
<h2 className={styles.header}>Connections</h2>
|
<h2 className={styles.header}>Connections</h2>
|
||||||
<div>
|
<div className={styles.authProviderList}>
|
||||||
<p>discord: {info.discordId}</p>
|
{providers.map((provider) => (
|
||||||
<p>github: {info.githubId}</p>
|
<AuthProviderEntry provider={provider} key={provider.name} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr className={styles.divider} />
|
<hr className={styles.divider} />
|
||||||
|
|
|
@ -2,6 +2,20 @@ import { getUser } from "@/auth/auth";
|
||||||
import { getUserInfo } from "@/ldap";
|
import { getUserInfo } from "@/ldap";
|
||||||
import AboutMe from "./AboutMe";
|
import AboutMe from "./AboutMe";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { DiscordAuthProvider } from "@/auth/discord";
|
||||||
|
import { GitHubAuthProvider } from "@/auth/github";
|
||||||
|
import { AuthProviderState } from "@/auth/AuthProvider";
|
||||||
|
|
||||||
|
// this sucks but i'm lazy
|
||||||
|
const discordFallback: AuthProviderState = {
|
||||||
|
name: "Discord",
|
||||||
|
connected: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const githubFallback: AuthProviderState = {
|
||||||
|
name: "GitHub",
|
||||||
|
connected: false
|
||||||
|
};
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const user = await getUser();
|
const user = await getUser();
|
||||||
|
@ -10,5 +24,13 @@ export default async function Page() {
|
||||||
const info = await getUserInfo(user);
|
const info = await getUserInfo(user);
|
||||||
if (info === null) redirect("/register");
|
if (info === null) redirect("/register");
|
||||||
|
|
||||||
return <AboutMe info={info} />;
|
const discord = await user.getDiscord();
|
||||||
|
const discordState = (await discord?.getState()) ?? discordFallback;
|
||||||
|
const github = await user.getGitHub();
|
||||||
|
const githubState = (await github?.getState()) ?? githubFallback;
|
||||||
|
|
||||||
|
const providers = [discordState, githubState];
|
||||||
|
|
||||||
|
// not sure how to feel about passing it like this
|
||||||
|
return <AboutMe info={info} providers={providers} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,18 +41,17 @@ export async function GET(request: Request) {
|
||||||
gluestickId ?? undefined
|
gluestickId ?? undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
const [user, authTicket] = await authTicketOAuth(userId);
|
if (gluestickId != null) {
|
||||||
|
|
||||||
if (user?.username !== null) {
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
"Set-Cookie": `ticket=${authTicket.ticket}; Path=/;`,
|
|
||||||
Location: "/me"
|
Location: "/me"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [user, authTicket] = await authTicketOAuth(userId);
|
||||||
|
|
||||||
const username = await provider.getDisplayName();
|
const username = await provider.getDisplayName();
|
||||||
const email = await provider.getEmail();
|
const email = await provider.getEmail();
|
||||||
const avatarUrl = await provider.getAvatar();
|
const avatarUrl = await provider.getAvatar();
|
||||||
|
|
|
@ -12,6 +12,14 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bail {
|
||||||
|
color: var(--fg-darker);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Input from "@/components/Input";
|
||||||
import PrettyForm from "@/components/PrettyForm";
|
import PrettyForm from "@/components/PrettyForm";
|
||||||
import HugeSubmit from "@/components/HugeSubmit";
|
import HugeSubmit from "@/components/HugeSubmit";
|
||||||
import AvatarChanger from "@/components/AvatarChanger";
|
import AvatarChanger from "@/components/AvatarChanger";
|
||||||
|
import { ValidAuthProvider } from "@/auth/AuthProvider";
|
||||||
|
|
||||||
type RegisterResponse = {
|
type RegisterResponse = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
|
@ -25,7 +26,7 @@ export default function RegisterForm({
|
||||||
initialDisplayName?: string;
|
initialDisplayName?: string;
|
||||||
initialEmail?: string;
|
initialEmail?: string;
|
||||||
initialAvatarBase64?: string;
|
initialAvatarBase64?: string;
|
||||||
avatarSource: "Discord" | "GitHub" | null;
|
avatarSource: ValidAuthProvider | null;
|
||||||
}) {
|
}) {
|
||||||
const [globalError, setGlobalError] = React.useState<string | null>(null);
|
const [globalError, setGlobalError] = React.useState<string | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -105,7 +106,6 @@ export default function RegisterForm({
|
||||||
label="Username"
|
label="Username"
|
||||||
placeholder="julian"
|
placeholder="julian"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
hint="Your display name - this can be what you go by online, for example."
|
hint="Your display name - this can be what you go by online, for example."
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -113,7 +113,6 @@ export default function RegisterForm({
|
||||||
label="Display name"
|
label="Display name"
|
||||||
placeholder="NotNite"
|
placeholder="NotNite"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
hint="Your email address. An inbox will be created on @n2.pm that forwards to this email."
|
hint="Your email address. An inbox will be created on @n2.pm that forwards to this email."
|
||||||
type="email"
|
type="email"
|
||||||
|
@ -121,7 +120,6 @@ export default function RegisterForm({
|
||||||
label="Email"
|
label="Email"
|
||||||
placeholder="hi@notnite.com"
|
placeholder="hi@notnite.com"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
hint="Your password. To secure NotNet services, make this a strong and long password."
|
hint="Your password. To secure NotNet services, make this a strong and long password."
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -131,7 +129,6 @@ export default function RegisterForm({
|
||||||
minLength={12}
|
minLength={12}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
|
@ -139,10 +136,9 @@ export default function RegisterForm({
|
||||||
placeholder="deeznuts47"
|
placeholder="deeznuts47"
|
||||||
minLength={12}
|
minLength={12}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
hint={
|
hint={
|
||||||
(avatarSource != null && avatarSource !== "Discord"
|
(avatarSource != null
|
||||||
? `We found your avatar from ${avatarSource}, but you can change it if you'd like.`
|
? `We found your avatar from ${avatarSource}, but you can change it if you'd like.`
|
||||||
: "") +
|
: "") +
|
||||||
" This will automatically be used as your avatar with supported services - maximum 1 MB. "
|
" This will automatically be used as your avatar with supported services - maximum 1 MB. "
|
||||||
|
@ -160,10 +156,21 @@ export default function RegisterForm({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={styles.buttonContainer}>
|
<div className={styles.buttonContainer}>
|
||||||
<HugeSubmit value="Join NotNet!" disabled={isSubmitting} />
|
<HugeSubmit value="Join NotNet!" disabled={isSubmitting} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
className={styles.bail}
|
||||||
|
onClick={async () => {
|
||||||
|
await fetch("/api/unlink", { method: "POST" });
|
||||||
|
document.cookie =
|
||||||
|
"ticket=; expires=" + new Date().toUTCString() + "; path=/";
|
||||||
|
window.location.href = "/";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{`Didn't mean to sign up?`}
|
||||||
|
</a>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
@ -3,8 +3,9 @@ import styles from "@/app/page.module.css";
|
||||||
import RegisterForm from "./RegisterForm";
|
import RegisterForm from "./RegisterForm";
|
||||||
import { redirect, useRouter } from "next/navigation";
|
import { redirect, useRouter } from "next/navigation";
|
||||||
import { ensureJpg } from "@/image";
|
import { ensureJpg } from "@/image";
|
||||||
|
import { ValidAuthProvider } from "@/auth/AuthProvider";
|
||||||
|
|
||||||
function avatarUrlSource(url: URL): "Discord" | "GitHub" | null {
|
function avatarUrlSource(url: URL): ValidAuthProvider | null {
|
||||||
if (
|
if (
|
||||||
url.hostname === "cdn.discordapp.com" &&
|
url.hostname === "cdn.discordapp.com" &&
|
||||||
url.pathname.startsWith("/avatars")
|
url.pathname.startsWith("/avatars")
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
export type ValidAuthProvider = "Discord" | "GitHub";
|
||||||
|
|
||||||
|
// Can't send the providers across the wire, do this instead
|
||||||
|
export type AuthProviderState = {
|
||||||
|
name: string;
|
||||||
|
} & ({ connected: false } | { connected: true; id: string; username: string });
|
||||||
|
|
||||||
export abstract class AuthProvider {
|
export abstract class AuthProvider {
|
||||||
protected readonly accessToken: string;
|
protected readonly accessToken: string;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { cookies } from "next/dist/client/components/headers";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import * as ldap from "../ldap";
|
import * as ldap from "../ldap";
|
||||||
import { getLogger } from "../logger";
|
import { getLogger } from "../logger";
|
||||||
import { AuthTicket, User } from "@prisma/client";
|
import { AuthTicket, DiscordAuth, User } from "@prisma/client";
|
||||||
import { DiscordAuthProvider } from "./discord";
|
import { DiscordAuthProvider } from "./discord";
|
||||||
import { GitHubAuthProvider } from "./github";
|
import { GitHubAuthProvider } from "./github";
|
||||||
import { AuthProvider } from "./AuthProvider";
|
import { AuthProvider } from "./AuthProvider";
|
||||||
|
@ -97,6 +97,26 @@ async function getAuthTicket() {
|
||||||
return ticket ?? null;
|
return ticket ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRegisteringUser(ticket?: AuthTicket) {
|
||||||
|
if (ticket == null) {
|
||||||
|
let newTicket = await getAuthTicket();
|
||||||
|
if (newTicket == null) return null;
|
||||||
|
ticket = newTicket;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: ticket.userId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
discordAuth: true,
|
||||||
|
githubAuth: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getUser(ticket?: AuthTicket) {
|
export async function getUser(ticket?: AuthTicket) {
|
||||||
if (ticket == null) {
|
if (ticket == null) {
|
||||||
let newTicket = await getAuthTicket();
|
let newTicket = await getAuthTicket();
|
||||||
|
@ -147,7 +167,7 @@ export async function getAuthState() {
|
||||||
if (ticket == null) return AuthState.LoggedOut;
|
if (ticket == null) return AuthState.LoggedOut;
|
||||||
|
|
||||||
const user = await getUser(ticket);
|
const user = await getUser(ticket);
|
||||||
if (user == null) return AuthState.Registering;
|
if (user == null || user.isRegistering) return AuthState.Registering;
|
||||||
|
|
||||||
return AuthState.LoggedIn;
|
return AuthState.LoggedIn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { AuthProvider } from "./AuthProvider";
|
import { AuthProvider, AuthProviderState } from "./AuthProvider";
|
||||||
import prisma from "@/prisma";
|
import prisma from "@/prisma";
|
||||||
|
|
||||||
export type DiscordAccessTokenResponse = {
|
export type DiscordAccessTokenResponse = {
|
||||||
|
@ -77,6 +77,18 @@ export class DiscordAuthProvider extends AuthProvider {
|
||||||
return me.email;
|
return me.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getState(): Promise<AuthProviderState> {
|
||||||
|
const username = await this.getUsername();
|
||||||
|
const id = await this.getId();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "Discord",
|
||||||
|
connected: true,
|
||||||
|
id,
|
||||||
|
username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static get redirectUri(): string {
|
static get redirectUri(): string {
|
||||||
return `${process.env.BASE_DOMAIN}oauth/discord/redirect`;
|
return `${process.env.BASE_DOMAIN}oauth/discord/redirect`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { AuthProvider } from "./AuthProvider";
|
import { AuthProvider, AuthProviderState } from "./AuthProvider";
|
||||||
import prisma from "@/prisma";
|
import prisma from "@/prisma";
|
||||||
|
|
||||||
export type GitHubAccessTokenResponse = {
|
export type GitHubAccessTokenResponse = {
|
||||||
|
@ -63,6 +63,18 @@ export class GitHubAuthProvider extends AuthProvider {
|
||||||
return me.email;
|
return me.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getState(): Promise<AuthProviderState> {
|
||||||
|
const username = await this.getUsername();
|
||||||
|
const id = await this.getId();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "GitHub",
|
||||||
|
connected: true,
|
||||||
|
id,
|
||||||
|
username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static get redirectUri(): string {
|
static get redirectUri(): string {
|
||||||
return `${process.env.BASE_DOMAIN}oauth/github/redirect`;
|
return `${process.env.BASE_DOMAIN}oauth/github/redirect`;
|
||||||
}
|
}
|
||||||
|
@ -111,6 +123,19 @@ export class GitHubAuthProvider extends AuthProvider {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: a.userId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
githubAuth: {
|
||||||
|
connect: {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return a.userId;
|
return a.userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue