Migrating content using the Ghost API to Offprint / Standard Site on atproto
ghostoff docs oauth_manual_testing.md
3.8 kB · 104 lines
Markdown
at main

Manually testing the OAuth login flow #

This guide walks through a real browser-based OAuth login for GhostOff.

1. Make sure there is no app password configured #

GhostOff defaults to OAuth whenever ATP_APP_PASSWORD (or --atproto-app-password) is absent. The easiest way to test that path is to unset it just for this run:

env ATP_APP_PASSWORD= npm run dev

Or temporarily comment out ATP_APP_PASSWORD in your .env.

If you want to keep this test isolated from your normal OAuth session files, pass different file names:

env ATP_APP_PASSWORD= npm run dev -- \
  --oauth-session-file ghostoff-oauth-session-test.json \
  --oauth-state-file ghostoff-oauth-state-test.json

ATP_SERVICE can be left set or removed; OAuth discovers the PDS from the handle.

2. Answer the prompt #

You should see:

No app password found in .env or --atproto-app-password. Would you like to authenticate via OAuth [Y/n]?

Type Y and press Enter.

If ATP_IDENTIFIER is not configured, the CLI will then prompt:

Enter your atproto handle (e.g. handle.example.com):

3. Authenticate in the browser #

The CLI starts a temporary HTTP server on 127.0.0.1:<ephemeral-port> and prints an authorization URL:

Open this URL in your browser to authenticate GhostOff:
https://selfhosted.social/oauth/...?request_uri=...&client_id=http%3A%2F%2Flocalhost%2F%3F...

The CLI also tries to open the URL automatically. If it doesn't, copy and paste it into your browser.

Log in to your PDS and approve GhostOff. Because this is a local-development OAuth client, the client_id defaults to http://localhost/?redirect_uri=... and the callback lands on http://127.0.0.1:<port>/callback.

4. Wait for the redirect #

After you approve, the PDS redirects to the local callback server. You should see:

Authentication successful. You can close this tab and return to the terminal.

In the terminal you should see something like:

authenticated as did:plc:...
session ready: your-handle.example.com (did:plc:...)

The migration then proceeds normally.

5. Verify it uploaded #

Once the run finishes, check your Offprint publication to confirm the posts appeared.

If the PDS rejects the default client_id #

The atproto local-development exception expects the client_id origin to be http://localhost and the callback to be 127.0.0.1. If your PDS insists on http://127.0.0.1 as the client_id origin, override it:

env ATP_APP_PASSWORD= npm run dev -- \
  --oauth-client-id "http://127.0.0.1/?redirect_uri=http%3A%2F%2F127.0.0.1%2Fcallback&scope=atproto%20transition%3Ageneric" \
  --oauth-session-file ghostoff-oauth-session-test.json \
  --oauth-state-file ghostoff-oauth-state-test.json

Re-testing the full flow #

To force a fresh login, delete the test session file and run again:

rm ghostoff-oauth-session-test.json
env ATP_APP_PASSWORD= npm run dev -- \
  --oauth-session-file ghostoff-oauth-session-test.json \
  --oauth-state-file ghostoff-oauth-state-test.json

To test session restore instead, leave the session file in place and run the same command a second time. It should skip the browser step and use the cached OAuth session.

Troubleshooting #

  • Browser doesn't open: copy the printed URL manually.
  • "OAuth state mismatch" or callback errors: make sure you didn't start a second GhostOff process while the first is still waiting for the callback; the loopback server only handles one callback.
  • Callback never reaches the terminal: ensure your browser is running on the same machine as the CLI, because the callback server binds to 127.0.0.1 only.
  • Migration prompts for a handle instead of using ATP_IDENTIFIER: ATP_IDENTIFIER was empty; the prompt is the intended fallback.