aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/app/.server/auth.ts
blob: 0c7742a568f4eadc36fa2bd3361c5718d6be3eab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import type { Session } from "@remix-run/server-runtime";
import { jwtDecode } from "jwt-decode";
import { Authenticator } from "remix-auth";
import { FormStrategy } from "remix-auth-form";
import { apiPostLogin } from "./api/client";
import { components } from "./api/schema";
import { sessionStorage } from "./session";

export const authenticator = new Authenticator<string>(sessionStorage);

async function login(username: string, password: string): Promise<string> {
	return (await apiPostLogin(username, password)).token;
}

authenticator.use(
	new FormStrategy(async ({ form }) => {
		const username = String(form.get("username"));
		const password = String(form.get("password"));
		return await login(username, password);
	}),
	"default",
);

export type User = components["schemas"]["User"];

export async function isAuthenticated(
	request: Request | Session,
	options?: {
		successRedirect?: never;
		failureRedirect?: never;
		headers?: never;
	},
): Promise<{ user: User; token: string } | null>;
export async function isAuthenticated(
	request: Request | Session,
	options: {
		successRedirect: string;
		failureRedirect?: never;
		headers?: HeadersInit;
	},
): Promise<null>;
export async function isAuthenticated(
	request: Request | Session,
	options: {
		successRedirect?: never;
		failureRedirect: string;
		headers?: HeadersInit;
	},
): Promise<{ user: User; token: string }>;
export async function isAuthenticated(
	request: Request | Session,
	options: {
		successRedirect: string;
		failureRedirect: string;
		headers?: HeadersInit;
	},
): Promise<null>;
export async function isAuthenticated(
	request: Request | Session,
	options:
		| {
				successRedirect?: never;
				failureRedirect?: never;
				headers?: never;
		  }
		| {
				successRedirect: string;
				failureRedirect?: never;
				headers?: HeadersInit;
		  }
		| {
				successRedirect?: never;
				failureRedirect: string;
				headers?: HeadersInit;
		  }
		| {
				successRedirect: string;
				failureRedirect: string;
				headers?: HeadersInit;
		  } = {},
): Promise<{ user: User; token: string } | null> {
	// This function's signature should be compatible with `authenticator.isAuthenticated` but TypeScript does not infer it correctly.
	let jwt;
	const { successRedirect, failureRedirect, headers } = options;
	if (successRedirect && failureRedirect) {
		jwt = await authenticator.isAuthenticated(request, {
			successRedirect,
			failureRedirect,
			headers,
		});
	} else if (!successRedirect && failureRedirect) {
		jwt = await authenticator.isAuthenticated(request, {
			failureRedirect,
			headers,
		});
	} else if (successRedirect && !failureRedirect) {
		jwt = await authenticator.isAuthenticated(request, {
			successRedirect,
			headers,
		});
	} else {
		jwt = await authenticator.isAuthenticated(request);
	}

	if (!jwt) {
		return null;
	}
	const user = jwtDecode<User>(jwt);
	return {
		user,
		token: jwt,
	};
}