this repo has no description
1<script lang="ts"> 2 import { getAuthState, logout } from '../lib/auth.svelte' 3 import { navigate } from '../lib/router.svelte' 4 import { generateCodeVerifier, generateCodeChallenge, saveOAuthState, generateState } from '../lib/oauth' 5 import { _ } from '../lib/i18n' 6 7 const auth = getAuthState() 8 let error = $state<string | null>(null) 9 let loading = $state(true) 10 let actAsInProgress = $state(false) 11 12 function getDid(): string | null { 13 const params = new URLSearchParams(window.location.search) 14 return params.get('did') 15 } 16 17 $effect(() => { 18 if (!auth.loading && !auth.session && !actAsInProgress) { 19 navigate('/login') 20 } 21 }) 22 23 $effect(() => { 24 if (auth.session && !actAsInProgress) { 25 actAsInProgress = true 26 initiateActAs() 27 } 28 }) 29 30 async function initiateActAs() { 31 const did = getDid() 32 if (!did) { 33 error = $_('actAs.noAccountSpecified') 34 loading = false 35 return 36 } 37 38 try { 39 const response = await fetch( 40 `/xrpc/_delegation.listControlledAccounts`, 41 { 42 headers: { 'Authorization': `Bearer ${auth.session!.accessJwt}` } 43 } 44 ) 45 46 if (!response.ok) { 47 error = $_('actAs.failedToVerify') 48 loading = false 49 return 50 } 51 52 const data = await response.json() 53 const account = data.accounts?.find((a: { did: string }) => a.did === did) 54 55 if (!account) { 56 error = $_('actAs.noAccess') 57 loading = false 58 return 59 } 60 61 await logout() 62 63 const hostname = window.location.origin 64 const state = generateState() 65 const codeVerifier = generateCodeVerifier() 66 const codeChallenge = await generateCodeChallenge(codeVerifier) 67 saveOAuthState({ state, codeVerifier }) 68 69 const parResponse = await fetch('/oauth/par', { 70 method: 'POST', 71 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 72 body: new URLSearchParams({ 73 client_id: `${hostname}/oauth/client-metadata.json`, 74 redirect_uri: `${hostname}/`, 75 response_type: 'code', 76 scope: 'atproto', 77 state: state, 78 code_challenge: codeChallenge, 79 code_challenge_method: 'S256', 80 login_hint: account.handle 81 }) 82 }) 83 84 if (!parResponse.ok) { 85 error = $_('actAs.failedToInitiate') 86 loading = false 87 return 88 } 89 90 const parData = await parResponse.json() 91 if (parData.request_uri) { 92 window.location.href = `/app/oauth/login?request_uri=${encodeURIComponent(parData.request_uri)}` 93 } else { 94 error = $_('actAs.invalidResponse') 95 loading = false 96 } 97 } catch (e) { 98 error = $_('actAs.failedError', { values: { error: e instanceof Error ? e.message : String(e) } }) 99 loading = false 100 } 101 } 102 103 function goBack() { 104 navigate('/controllers') 105 } 106</script> 107 108<div class="page"> 109 {#if loading} 110 <div class="loading"> 111 <p>{$_('actAs.preparing')}</p> 112 </div> 113 {:else} 114 <header> 115 <h1>{$_('actAs.title')}</h1> 116 </header> 117 118 {#if error} 119 <div class="message error">{error}</div> 120 {/if} 121 122 <div class="actions"> 123 <button class="back-btn" onclick={goBack}> 124 {$_('actAs.backToControllers')} 125 </button> 126 </div> 127 {/if} 128</div> 129 130<style> 131 .page { 132 max-width: var(--width-md); 133 margin: var(--space-9) auto; 134 padding: var(--space-7); 135 } 136 137 .loading { 138 display: flex; 139 align-items: center; 140 justify-content: center; 141 min-height: 200px; 142 color: var(--text-secondary); 143 } 144 145 header { 146 margin-bottom: var(--space-6); 147 } 148 149 h1 { 150 margin: 0; 151 } 152 153 .message.error { 154 padding: var(--space-3); 155 background: var(--error-bg); 156 border: 1px solid var(--error-border); 157 border-radius: var(--radius-md); 158 color: var(--error-text); 159 margin-bottom: var(--space-4); 160 } 161 162 .actions { 163 margin-top: var(--space-4); 164 } 165 166 .back-btn { 167 padding: var(--space-3) var(--space-5); 168 border: 1px solid var(--border-color); 169 border-radius: var(--radius-md); 170 background: transparent; 171 color: var(--text-primary); 172 cursor: pointer; 173 } 174 175 .back-btn:hover { 176 background: var(--bg-card); 177 border-color: var(--accent); 178 } 179</style>