this repo has no description
1# Expo Atproto OAuth 2 3This is an Expo client library for Atproto OAuth. It implements the required native crypto functions for supporting JWTs in React Native and uses 4the base `OAuthClient` interface found in [the Atproto repository](https://github.com/bluesky-social/atproto/tree/main/packages/oauth/oauth-client). 5 6## Prerequisites 7 8Before using this library, there are a few additional libraries that you must install within your Expo application. 9 10- [react-native-mmkv](https://www.npmjs.com/package/react-native-mmkv) 11- [expo-web-browser](https://www.npmjs.com/package/expo-web-browser) 12- [@atproto/oauth-client](https://www.npmjs.com/package/@atproto/oauth-client) 13- [event-target-polyfill](https://www.npmjs.com/package/event-target-polyfill) (or similar) 14- [abortcontroller-polyfill](https://www.npmjs.com/package/abortcontroller-polyfill) (or similar) 15 16Apply the two polyfills inside your application's entrypoint (usually `index.ts`). They should be placed _before_ anything else in the file, and particularly before `registerRootComponent(App)`. 17 18> [!CAUTION] 19> As of current (Expo 53), you _must_ apply an Expo patch for this library to work. You may use the patch found [here](https://github.com/haileyok/expo-atproto-auth/blob/main/patches/expo%2B53.0.19.patch). 20A fix for this has been submitted up stream and merged, so will hopefully be fixed in Expo 54 (see the PR [here](https://github.com/expo/expo/pull/38122)). 21 22### In bare React Native projects 23 24For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) 25before continuing. 26 27## Installation 28 29Once you have satisfied the prerequisites, you can simply install the library with `yarn add expo-atproto-auth`. 30 31## Usage 32 33### Serve your `oauth-client-metadata.json` 34 35You will need to server an `oauth-client-metadata.json` from your application's website. An example of this metadata 36would look like this: 37 38``` 39{ 40 "client_id": "https://hailey.at/oauth-client-metadata.json", 41 "client_name": "React Native OAuth Client Demo", 42 "client_uri": "https://hailey.at", 43 "redirect_uris": [ 44 "at.hailey:/auth/callback" 45 ], 46 "scope": "atproto transition:generic", 47 "token_endpoint_auth_method": "none", 48 "response_types": [ 49 "code" 50 ], 51 "grant_types": [ 52 "authorization_code", 53 "refresh_token" 54 ], 55 "application_type": "native", 56 "dpop_bound_access_tokens": true 57} 58``` 59 60- The `client_id` should be the same URL as where you are serving your `oauth-client-metadata.json` from 61- The `client_uri` can be the home page of where you are serving your metadata from 62- Your `redirect_uris` should contain the native redirect URI in the first position. Additionally, the scheme must be 63formatted as the _reverse_ of the domain you are serving the metadata from. Since I am serving mine from `hailey.at`, 64I use `at.hailey` as the scheme. If my domain were `atproto.expo.dev`, I would use `dev.expo.atproto`. Additionally, the scheme _must_ contain _only one trailing slash_ after the `:`. `at.hailey://` would be invalid. 65- The `application_type` must be `native` 66 67For a real-world example, see [Skylight's client metadata](https://skylight.expo.app/oauth/client-metadata.json). 68 69For more information about client metadata, see [the Atproto documentation](https://atproto.com/specs/oauth#client-id-metadata-document). 70 71### Create a client 72 73Next, you want to create an `ExpoOAuthClient`. You will need to pass in the same client metadata to the client as you are serving in your `oauth-client-metadata.json`. 74 75```ts 76const client = new ExpoOAuthClient({ 77 clientMetadata: { 78 client_id: 'https://hailey.at/oauth-client-metadata.json', 79 client_name: 'React Native OAuth Client Demo', 80 client_uri: 'https://hailey.at', 81 redirect_uris: ['at.hailey:/auth/callback'], 82 scope: 'atproto transition:generic', 83 token_endpoint_auth_method: 'none', 84 response_types: ['code'], 85 grant_types: ['authorization_code', 'refresh_token'], 86 application_type: 'native', 87 dpop_bound_access_tokens: true, 88 }, 89 handleResolver: 'https://bsky.social', 90}) 91``` 92 93### Sign a user in 94 95Whenever you are ready, you can initiate a sign in attempt for the user using the client using `client.signIn(input)` 96 97`input` must be one of the following: 98- A valid Atproto user handle, e.g. `hailey.bsky.team` or `hailey.at` 99- A valid DID, e.g. `did:web:hailey.at` or `did:plc:oisofpd7lj26yvgiivf3lxsi` 100- A valid PDS host, e.g. `https://cocoon.hailey.at` or `https://bsky.social` 101 102> [!NOTE] 103> If you wish to allow a user to _create_ an account instead of signing in, simply use a valid PDS hostname rather than 104> a handle. They will be presented the option to either Sign In with an existing account, or create a new one. 105 106The response of `signIn` will be a promise resolving to the following: 107 108```ts 109 | { status: WebBrowserResultType } // See Expo Web Browser documentation 110 | { status: 'error'; error: unknown } 111 | { status: 'success'; session: OAuthSession } 112``` 113 114For example: 115 116```ts 117 const res = await client.signIn(input ?? '') 118 if (res.status === 'success') { 119 setSession(res.session) 120 const newAgent = new Agent(res.session) 121 setAgent(newAgent) 122 } else if (res.status === 'error') { 123 Alert.alert('Error', (res.error as any).toString()) 124 } else { 125 Alert.alert( 126 'Error', 127 `Received unknown WebResultType: ${res.status}` 128 ) 129 } 130``` 131 132### Create an `Agent` 133 134To interface with the various Atproto APIs, you will need to create an `Agent`. You will pass your `OAuthSession` to the `Agent`. 135 136```ts 137const newAgent = new Agent(res.session) 138``` 139 140Session refreshes will be handled for you for the lifetime of the agent. 141 142### Restoring a session 143 144After, for example, closing the application, you will probably need to restore the user's session. You can do this by using the user's DID on the `ExpoOAuthClient`. 145 146```ts 147const restoreRes = await client.restore('did:plc:oisofpd7lj26yvgiivf3lxsi') 148const newAgent = new Agent(restoreRes) 149``` 150 151If the session needs to be refreshed, `.restore()` will do this for you before returning a session. 152 153## Additional Reading 154 155- [Atproto OAuth Spec](https://atproto.com/specs/oauth) 156- [Atproto Web OAuth Example](https://github.com/bluesky-social/atproto/tree/main/packages/oauth/oauth-client-browser-example)