g1t/packages/contracts/src/identity.ts

92 lines3,709 bytesCodeBlame
1import type { Result } from "./result";
2
3export type User = {
4 id: string;
5 username: string;
6 /**
7 * Whether the account's email address is confirmed. Only set on users
8 * resolved from credentials; unverified accounts cannot change anything.
9 */
10 verified?: boolean;
11};
12
13/** Who is asking. Every read and write in every service takes one. */
14export type Viewer = User | null;
15
16export type SshKey = {
17 id: string;
18 title: string;
19 fingerprint: string;
20 /** RFC 3339. */
21 createdAt: string;
22};
23
24export type AccessToken = { id: string; name: string; createdAt: string };
25
26export type DeviceStart = {
27 /** Secret held by the tool and exchanged for a token once approved. */
28 deviceCode: string;
29 /** Short code shown to the person, e.g. `WDJB-MJHT`. */
30 userCode: string;
31 /** Seconds until both codes stop working. */
32 expiresIn: number;
33 /** Seconds the tool should wait between polls. */
34 interval: number;
35};
36
37export type DeviceRequest = { userCode: string; clientName: string };
38
39export type DeviceClaim =
40 | { status: "pending" | "denied" | "expired" }
41 | { status: "approved"; token: string; user: User };
42
43/** Accounts, credentials and sessions. */
44export interface IdentityApi {
45 /** Creates an account and signs it in. */
46 register(username: string, email: string, password: string): Promise<Result<{ user: User; sessionToken: string }>>;
47 /** Verifies a username and password for website sign-in. */
48 signIn(username: string, password: string): Promise<Result<{ user: User; sessionToken: string }>>;
49 signOut(sessionToken: string): Promise<void>;
50
51 /** Sends the confirmation email again. */
52 resendVerification(user: User): Promise<Result<boolean>>;
53 /** Confirms the address the emailed token was sent to. */
54 verifyEmail(token: string): Promise<Result<User>>;
55 /** Emails a reset link if the address has an account. Always resolves. */
56 requestPasswordReset(email: string): Promise<boolean>;
57 /** Sets a new password from an emailed token and ends every session. */
58 resetPassword(token: string, password: string): Promise<Result<User>>;
59
60 /**
61 * Device sign-in (RFC 8628). A tool starts a request, a person approves
62 * its short code in a browser, and the tool claims an access token.
63 */
64 deviceStart(clientName: string): Promise<DeviceStart>;
65 /** What a user code is asking for, or null if it is not valid. */
66 deviceLookup(userCode: string): Promise<DeviceRequest | null>;
67 deviceResolve(userCode: string, user: User, approve: boolean): Promise<Result<boolean>>;
68 deviceClaim(deviceCode: string): Promise<DeviceClaim>;
69
70 userForSession(sessionToken: string): Promise<Viewer>;
71
72 /** Verifies git credentials: the account password or an access token. */
73 userForGitCredentials(username: string, secret: string): Promise<Viewer>;
74 /** Resolves a `g1t_…` access token, as sent to the API and MCP server. */
75 userForAccessToken(token: string): Promise<Viewer>;
76 userForSshKey(fingerprint: string): Promise<Viewer>;
77 userByUsername(username: string): Promise<Viewer>;
78
79 listSshKeys(user: User): Promise<SshKey[]>;
80 /** Takes one line in OpenSSH public key format. */
81 addSshKey(user: User, title: string, publicKey: string): Promise<Result<SshKey>>;
82 removeSshKey(user: User, id: string): Promise<void>;
83
84 listAccessTokens(user: User): Promise<AccessToken[]>;
85 /** The plaintext token is returned once and never stored. */
86 /**
87 * With `ttlSeconds` the token expires and is left out of the user's list;
88 * that form is used for hosted attempts.
89 */
90 createAccessToken(user: User, name: string, ttlSeconds?: number): Promise<{ token: string; info: AccessToken }>;
91 removeAccessToken(user: User, id: string): Promise<void>;
92}