this repo has no description
1import {
2 type Fetch,
3 type OAuthClientMetadataInput,
4 type OAuthClientOptions,
5 type OAuthResponseMode,
6 atprotoLoopbackClientMetadata,
7 OAuthClient,
8 OAuthSession,
9} from '@atproto/oauth-client'
10import { ReactNativeRuntimeImplementation } from './react-native-runtime-implementation'
11import { ReactNativeOAuthDatabase } from './react-native-oauth-database'
12import { openAuthSessionAsync, WebBrowserResultType } from 'expo-web-browser'
13
14export type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown>
15
16export type ReactNativeOAuthClientOptions = Simplify<
17 {
18 clientMetadata?: Readonly<OAuthClientMetadataInput>
19 responseMode?: Exclude<OAuthResponseMode, 'form_post'>
20 fetch?: Fetch
21 } & Omit<
22 OAuthClientOptions,
23 | 'clientMetadata'
24 | 'responseMode'
25 | 'keyset'
26 | 'fetch'
27 | 'runtimeImplementation'
28 | 'sessionStore'
29 | 'stateStore'
30 | 'didCache'
31 | 'handleCache'
32 | 'dpopNonceCache'
33 | 'authorizationServerMetadataCache'
34 | 'protectedResourceMetadataCache'
35 >
36>
37
38export class ExpoOAuthClient extends OAuthClient {
39 constructor({
40 responseMode = 'fragment',
41 ...options
42 }: ReactNativeOAuthClientOptions) {
43 const database = new ReactNativeOAuthDatabase()
44
45 if (!['query', 'fragment'].includes(responseMode)) {
46 throw new TypeError(`Invalid response mode: ${responseMode}`)
47 }
48
49 if (!options.clientMetadata) {
50 throw new TypeError(`No client metadata provided`)
51 }
52
53 super({
54 ...options,
55 clientMetadata:
56 options.clientMetadata ?? atprotoLoopbackClientMetadata('localhost'), // HACK: this fixes a type error for now, look into it later
57 responseMode,
58 keyset: undefined,
59 runtimeImplementation: new ReactNativeRuntimeImplementation(),
60 sessionStore: database.getSessionStore(),
61 stateStore: database.getStateStore(),
62 didCache: database.getDidCache(),
63 handleCache: database.getHandleCache(),
64 dpopNonceCache: database.getDpopNonceCache(),
65 authorizationServerMetadataCache:
66 database.getAuthorizationServerMetadataCache(),
67 protectedResourceMetadataCache:
68 database.getProtectedResourceMetadataCache(),
69 })
70 }
71
72 async signIn(
73 input: string
74 ): Promise<
75 | { status: WebBrowserResultType }
76 | { status: 'error'; error: unknown }
77 | { status: 'success'; session: OAuthSession }
78 > {
79 let url: URL
80 try {
81 url = await this.authorize(input)
82 } catch (e: unknown) {
83 return { status: 'error', error: e }
84 }
85
86 const res = await openAuthSessionAsync(
87 url.toString(),
88 this.clientMetadata.redirect_uris[0],
89 {
90 createTask: false,
91 }
92 )
93
94 if (res.type === 'success') {
95 const resUrl = new URL(res.url)
96 const params = new URLSearchParams(resUrl.hash.substring(1))
97 const callbackRes = await this.callback(params)
98 return { status: 'success', session: callbackRes.session }
99 } else {
100 return { status: res.type }
101 }
102 }
103}