This is a guide to getting started with AT Protocol development with Expo. We've recently published a new library, @atproto/oauth-client-expo, which greatly simplifies the process of integrating atproto OAuth into your Expo app.

Installation

We’re going to use the example application from bluesky-social/cookbook, which is is based on a create-expo-app template.

npx giget@latest gh:bluesky-social/cookbook/react-native-oauth my-atproto-app

Then install dependencies and install the app on your phone/simulator/emulator:

cd my-atproto-app
bun i
bun ios

You then should see the login screen. Yay!

However, we need to do a bit more work before we can log in. This is because we need to host our OAuth metadata file and replace the placeholder variables so that it points to our app.

The OAuth metadata file (oauth-client-metadata.json) specifies what our app is, what permissions it will be asking for, and other details. This needs to be accessible on the web - luckily, we can use Expo Router API Routes for this!

First, change app.json to enable API routes:

"web": {
  "output": "server"
}

Then, go add an API route to deploy our metadata file at app/oauth-client-metadata.json+api.ts

export function GET(request: Request) {
  return Response.json(require("../oauth-client-metadata.json"));
}

Next, an awkward little step: the bundler will try and include some client only code, and fail. Change utils/oauthClient.ts to do some platform checks:

import { ExpoOAuthClient } from "@atproto/oauth-client-expo";
import { Platform } from "react-native";

export const oauthClient = (
  Platform.OS === "web" ? typeof window !== "undefined" : true
)
  ? new ExpoOAuthClient({
      handleResolver: "https://bsky.social",
      clientMetadata: require("../oauth-client-metadata.json"),
    })
  : (null as ExpoOAuthClient);

You can now export the server bundle using bunx expo export --platform web --no-ssg, then deploy it using eas deploy. It will ask you to configure a preview URL - choose a name, and remember it, because we’ll use it for our metadata. In this guide, I’m going to use atexample.expo.app - replace this with whatever you picked.

Now we have a domain name, we can tweak our metadata file to use it - you need to change client_id, client_uri, and redirect_uris. Note that redirect_uris must be the domain name in reverse notation.

{
  "client_id": "https://atexample.expo.app/oauth-client-metadata.json",
  "client_name": "React Native OAuth Client Demo",
  "client_uri": "https://atexample.expo.app",
  "redirect_uris": ["app.expo.atexample:/sign-in"],
  "scope": "atproto account:email rpc:*?aud=did:web:api.bsky.app#bsky_appview",
  "token_endpoint_auth_method": "none",
  "response_types": ["code"],
  "grant_types": ["authorization_code", "refresh_token"],
  "application_type": "native",
  "dpop_bound_access_tokens": true
}

We then need to go back to app.json and change the scheme to include the reversed domain name. You can set multiple schemes if you want to use a different scheme for your deep links down the line.

{
  "expo": {
    "name": "AT Example",
    "slug": "atexample",
    "scheme": ["atexample", "app.expo.atexample"],
    // etc etc
  }
}

Now the app will be able to receive the callback once the OAuth flow is completed. The final step is to deploy our updated metadata file, and we should be good to go!

bunx expo export --platform web --no-ssg
bunx eas deploy --prod

Once deployed, we can try logging in. If you have an Atmosphere/Bluesky account, you can use that - otherwise, you can make an account on the bsky.social PDS. It will automatically detect where your data is hosted and take you straight there. If you self-host a PDS, it will authenticate directly with your PDS.

Once you enter your password, you can see the permissions that the app is requesting. In our case, our metadata file specifies that the app wants to see what our account’s email is and let the app perform actions related to Bluesky on our behalf.

Press “Authorize”, and you’re in! You can see a very basic page that just pulls in your Bluesky name and profile picture.

This is a great jumping-off point for making your own Atmosphere-connected application. Interoperate with Bluesky, or carve your own path and build the future of social.

Until next time!