aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/app/.server/auth.ts
blob: 3d9a4920a262ad5a6128fe3444d40a551d6866aa (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,
  };
}