182 lines
4.5 KiB
JavaScript
182 lines
4.5 KiB
JavaScript
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
|
|
import { Agent } from '@atproto/api'
|
|
import { env } from '../config/env.js'
|
|
import { isSyuIsHandle } from '../utils/pds.js'
|
|
|
|
export class OAuthService {
|
|
constructor() {
|
|
this.clientId = env.oauth.clientId || this.getClientId()
|
|
this.clients = { bsky: null, syu: null }
|
|
this.agent = null
|
|
this.sessionInfo = null
|
|
this.initPromise = null
|
|
}
|
|
|
|
getClientId() {
|
|
const origin = window.location.origin
|
|
return origin.includes('localhost') || origin.includes('127.0.0.1')
|
|
? undefined // Loopback client
|
|
: `${origin}/client-metadata.json`
|
|
}
|
|
|
|
async initialize() {
|
|
if (this.initPromise) return this.initPromise
|
|
|
|
this.initPromise = this._initialize()
|
|
return this.initPromise
|
|
}
|
|
|
|
async _initialize() {
|
|
try {
|
|
// Initialize OAuth clients
|
|
this.clients.bsky = await BrowserOAuthClient.load({
|
|
clientId: this.clientId,
|
|
handleResolver: 'https://bsky.social',
|
|
plcDirectoryUrl: 'https://plc.directory',
|
|
})
|
|
|
|
this.clients.syu = await BrowserOAuthClient.load({
|
|
clientId: this.clientId,
|
|
handleResolver: 'https://syu.is',
|
|
plcDirectoryUrl: 'https://plc.syu.is',
|
|
})
|
|
|
|
// Try to restore session
|
|
return await this.restoreSession()
|
|
} catch (error) {
|
|
console.error('OAuth initialization failed:', error)
|
|
this.initPromise = null
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async restoreSession() {
|
|
// Try both clients
|
|
for (const client of [this.clients.bsky, this.clients.syu]) {
|
|
const result = await client.init()
|
|
if (result?.session) {
|
|
this.agent = new Agent(result.session)
|
|
return this.processSession(result.session)
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
async processSession(session) {
|
|
const did = session.sub || session.did
|
|
let handle = session.handle || 'unknown'
|
|
let displayName = null
|
|
let avatar = null
|
|
|
|
// Create Agent directly with session (per official docs)
|
|
try {
|
|
this.agent = new Agent(session)
|
|
} catch (err) {
|
|
// Fallback to dpopFetch method
|
|
this.agent = new Agent({
|
|
service: session.server?.serviceEndpoint || 'https://bsky.social',
|
|
fetch: session.dpopFetch
|
|
})
|
|
}
|
|
|
|
// Get profile information using authenticated agent
|
|
// Skip test DIDs
|
|
if (this.agent && did && !did.includes('test-')) {
|
|
try {
|
|
await new Promise(resolve => setTimeout(resolve, 300))
|
|
const profile = await this.agent.getProfile({ actor: did })
|
|
handle = profile.data.handle || handle
|
|
displayName = profile.data.displayName || null
|
|
avatar = profile.data.avatar || null
|
|
|
|
console.log('Profile fetched from session:', {
|
|
did,
|
|
handle,
|
|
displayName,
|
|
avatar: avatar ? 'present' : 'none'
|
|
})
|
|
} catch (error) {
|
|
console.log('Failed to get profile from session:', error)
|
|
// Keep the basic info we have
|
|
}
|
|
} else if (did && did.includes('test-')) {
|
|
console.log('Skipping profile fetch for test DID:', did)
|
|
}
|
|
|
|
this.sessionInfo = {
|
|
did,
|
|
handle,
|
|
displayName,
|
|
avatar
|
|
}
|
|
|
|
return {
|
|
did,
|
|
handle,
|
|
displayName,
|
|
avatar
|
|
}
|
|
}
|
|
|
|
async login(handle) {
|
|
await this.initialize()
|
|
|
|
const client = isSyuIsHandle(handle) ? this.clients.syu : this.clients.bsky
|
|
const authUrl = await client.authorize(handle, {
|
|
scope: 'atproto transition:generic'
|
|
})
|
|
|
|
window.location.href = authUrl.toString()
|
|
}
|
|
|
|
async checkAuth() {
|
|
try {
|
|
await this.initialize()
|
|
if (this.sessionInfo) {
|
|
return {
|
|
user: this.sessionInfo,
|
|
agent: this.agent
|
|
}
|
|
}
|
|
return null
|
|
} catch (error) {
|
|
console.error('Auth check failed:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
async logout() {
|
|
try {
|
|
// Sign out from session
|
|
if (this.clients.bsky) {
|
|
const result = await this.clients.bsky.init()
|
|
if (result?.session?.signOut) {
|
|
await result.session.signOut()
|
|
}
|
|
}
|
|
|
|
// Clear state
|
|
this.agent = null
|
|
this.sessionInfo = null
|
|
this.clients = { bsky: null, syu: null }
|
|
this.initPromise = null
|
|
|
|
// Clear storage
|
|
localStorage.clear()
|
|
sessionStorage.clear()
|
|
|
|
// Reload page
|
|
window.location.reload()
|
|
} catch (error) {
|
|
console.error('Logout failed:', error)
|
|
}
|
|
}
|
|
|
|
getAgent() {
|
|
return this.agent
|
|
}
|
|
|
|
getUser() {
|
|
return this.sessionInfo
|
|
}
|
|
} |