feat: create atproto oauth login

This commit is contained in:
Julien Calixte
2026-03-10 12:27:35 +01:00
parent 908641e54b
commit 8843d67a80
16 changed files with 485 additions and 7 deletions

View File

@@ -0,0 +1,30 @@
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
const CLIENT_ID = import.meta.env.DEV
? 'http://localhost'
: 'https://remanso.space/client-metadata.json'
let clientPromise: Promise<BrowserOAuthClient> | null = null
export const getOAuthClient = (): Promise<BrowserOAuthClient> => {
if (!clientPromise) {
clientPromise = BrowserOAuthClient.load({ clientId: CLIENT_ID })
}
return clientPromise
}
export const signInWithHandle = async (handle: string): Promise<void> => {
const client = await getOAuthClient()
await client.signInRedirect(handle, { scope: 'atproto transition:generic' })
}
export const restoreSession = async () => {
const client = await getOAuthClient()
const result = await client.init()
return result?.session ?? null
}
export const sdkSignOut = async (sub: string): Promise<void> => {
const client = await getOAuthClient()
await client.revoke(sub)
}

View File

@@ -0,0 +1,23 @@
import { data } from '@/data/data'
import { DataType } from '@/data/DataType.enum'
import { AtprotoSession } from '@/data/models/AtprotoSession'
const SESSION_ID = `${DataType.AtprotoSession}-current`
export const loadSession = (): Promise<AtprotoSession | null> => {
return data.get<DataType.AtprotoSession, AtprotoSession>(SESSION_ID)
}
export const saveSession = async (did: string, handle: string): Promise<void> => {
const session: AtprotoSession = {
_id: SESSION_ID,
$type: DataType.AtprotoSession,
did,
handle,
}
await data.update<DataType.AtprotoSession, AtprotoSession>(session)
}
export const clearSession = (): Promise<boolean> => {
return data.remove(SESSION_ID)
}

View File

@@ -0,0 +1,24 @@
export const getFollows = async (did: string): Promise<Set<string>> => {
const follows = new Set<string>()
let cursor: string | undefined
do {
const url = new URL('https://public.api.bsky.app/xrpc/app.bsky.graph.getFollows')
url.searchParams.set('actor', did)
url.searchParams.set('limit', '100')
if (cursor) {
url.searchParams.set('cursor', cursor)
}
const response = await fetch(url)
const result: { follows: { did: string }[]; cursor?: string } = await response.json()
for (const follow of result.follows) {
follows.add(follow.did)
}
cursor = result.cursor
} while (cursor)
return follows
}