this repo has no description
Kotlin 33.3%
Ignore List 16.7%
EditorConfig 8.3%
Gradle 8.3%
JavaScript 8.3%
Markdown 8.3%
Text 8.3%
XML 8.3%
6 1 0

Clone this repository

https://tangled.org/hailey.at/expo-atproto-auth
git@knot.hailey.at:hailey.at/expo-atproto-auth

For self-hosted knots, clone URLs may differ based on your setup.

README.md

Expo Atproto OAuth#

This is an Expo client library for Atproto OAuth. It implements the required native crypto functions for supporting JWTs in React Native and uses the base OAuthClient interface found in the Atproto repository.

Prerequisites#

Before using this library, there are a few additional libraries that you must install within your Expo application.

Apply 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).

CAUTION

As of current (Expo 53), you must apply an Expo patch for this library to work. You may use the patch found here. A fix for this has been submitted up stream and merged, so will hopefully be fixed in Expo 54 (see the PR here).

In bare React Native projects#

For bare React Native projects, you must ensure that you have installed and configured the expo package before continuing.

Installation#

Once you have satisfied the prerequisites, you can simply install the library with yarn add expo-atproto-auth.

Usage#

Serve your oauth-client-metadata.json#

You will need to server an oauth-client-metadata.json from your application's website. An example of this metadata would look like this:

{
	"client_id": "https://hailey.at/oauth-client-metadata.json",
	"client_name": "React Native OAuth Client Demo",
	"client_uri": "https://hailey.at",
	"redirect_uris": [
		"at.hailey:/auth/callback"
	],
	"scope": "atproto transition:generic",
	"token_endpoint_auth_method": "none",
	"response_types": [
		"code"
	],
	"grant_types": [
		"authorization_code",
		"refresh_token"
	],
	"application_type": "native",
	"dpop_bound_access_tokens": true
}
  • The client_id should be the same URL as where you are serving your oauth-client-metadata.json from
  • The client_uri can be the home page of where you are serving your metadata from
  • Your redirect_uris should contain the native redirect URI in the first position. Additionally, the scheme must be formatted as the reverse of the domain you are serving the metadata from. Since I am serving mine from hailey.at, I 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.
  • The application_type must be native

For a real-world example, see Skylight's client metadata.

For more information about client metadata, see the Atproto documentation.

Create a client#

Next, 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.

const client = new ExpoOAuthClient({
  clientMetadata: {
    client_id: 'https://hailey.at/oauth-client-metadata.json',
    client_name: 'React Native OAuth Client Demo',
    client_uri: 'https://hailey.at',
    redirect_uris: ['at.hailey:/auth/callback'],
    scope: 'atproto transition:generic',
    token_endpoint_auth_method: 'none',
    response_types: ['code'],
    grant_types: ['authorization_code', 'refresh_token'],
    application_type: 'native',
    dpop_bound_access_tokens: true,
  },
  handleResolver: 'https://bsky.social',
})

Sign a user in#

Whenever you are ready, you can initiate a sign in attempt for the user using the client using client.signIn(input)

input must be one of the following:

  • A valid Atproto user handle, e.g. hailey.bsky.team or hailey.at
  • A valid DID, e.g. did:web:hailey.at or did:plc:oisofpd7lj26yvgiivf3lxsi
  • A valid PDS host, e.g. https://cocoon.hailey.at or https://bsky.social
NOTE

If you wish to allow a user to create an account instead of signing in, simply use a valid PDS hostname rather than a handle. They will be presented the option to either Sign In with an existing account, or create a new one.

The response of signIn will be a promise resolving to the following:

    | { status: WebBrowserResultType } // See Expo Web Browser documentation
    | { status: 'error'; error: unknown }
    | { status: 'success'; session: OAuthSession }

For example:

  const res = await client.signIn(input ?? '')
  if (res.status === 'success') {
    setSession(res.session)
    const newAgent = new Agent(res.session)
    setAgent(newAgent)
  } else if (res.status === 'error') {
    Alert.alert('Error', (res.error as any).toString())
  } else {
    Alert.alert(
      'Error',
      `Received unknown WebResultType: ${res.status}`
    )
  }

Create an Agent#

To interface with the various Atproto APIs, you will need to create an Agent. You will pass your OAuthSession to the Agent.

const newAgent = new Agent(res.session)

Session refreshes will be handled for you for the lifetime of the agent.

Restoring a session#

After, 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.

const restoreRes = await client.restore('did:plc:oisofpd7lj26yvgiivf3lxsi')
const newAgent = new Agent(restoreRes)

If the session needs to be refreshed, .restore() will do this for you before returning a session.

Additional Reading#