about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/screens/Deactivated.tsx63
-rw-r--r--src/screens/Settings/components/DeactivateAccountDialog.tsx84
-rw-r--r--src/state/persisted/schema.ts9
-rw-r--r--src/state/session/__tests__/session-test.ts61
-rw-r--r--src/state/session/agent.ts9
5 files changed, 211 insertions, 15 deletions
diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx
index faee517cb..add550f93 100644
--- a/src/screens/Deactivated.tsx
+++ b/src/screens/Deactivated.tsx
@@ -4,18 +4,27 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useFocusEffect} from '@react-navigation/native'
+import {useQueryClient} from '@tanstack/react-query'
 
 import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
+import {logger} from '#/logger'
 import {isWeb} from '#/platform/detection'
-import {type SessionAccount, useSession, useSessionApi} from '#/state/session'
+import {
+  type SessionAccount,
+  useAgent,
+  useSession,
+  useSessionApi,
+} from '#/state/session'
 import {useSetMinimalShellMode} from '#/state/shell'
 import {useLoggedOutViewControls} from '#/state/shell/logged-out'
 import {ScrollView} from '#/view/com/util/Views'
 import {Logo} from '#/view/icons/Logo'
 import {atoms as a, useTheme} from '#/alf'
 import {AccountList} from '#/components/AccountList'
-import {Button, ButtonText} from '#/components/Button'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {Divider} from '#/components/Divider'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {Loader} from '#/components/Loader'
 import {Text} from '#/components/Typography'
 
 const COL_WIDTH = 400
@@ -30,6 +39,10 @@ export function Deactivated() {
   const hasOtherAccounts = accounts.length > 1
   const setMinimalShellMode = useSetMinimalShellMode()
   const {logout} = useSessionApi()
+  const agent = useAgent()
+  const [pending, setPending] = React.useState(false)
+  const [error, setError] = React.useState<string | undefined>()
+  const queryClient = useQueryClient()
 
   useFocusEffect(
     React.useCallback(() => {
@@ -62,6 +75,34 @@ export function Deactivated() {
     logout('Deactivated')
   }, [logout])
 
+  const handleActivate = React.useCallback(async () => {
+    try {
+      setPending(true)
+      await agent.com.atproto.server.activateAccount()
+      await queryClient.resetQueries()
+      await agent.resumeSession(agent.session!)
+    } catch (e: any) {
+      switch (e.message) {
+        case 'Bad token scope':
+          setError(
+            _(
+              msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`,
+            ),
+          )
+          break
+        default:
+          setError(_(msg`Something went wrong, please try again`))
+          break
+      }
+
+      logger.error(e, {
+        context: 'Failed to activate account',
+      })
+    } finally {
+      setPending(false)
+    }
+  }, [_, agent, setPending, setError, queryClient])
+
   return (
     <View style={[a.h_full_vh, a.flex_1, t.atoms.bg]}>
       <ScrollView
@@ -104,10 +145,11 @@ export function Deactivated() {
                     size="medium"
                     variant="solid"
                     color="primary"
-                    onPress={() => setShowLoggedOut(true)}>
+                    onPress={handleActivate}>
                     <ButtonText>
                       <Trans>Yes, reactivate my account</Trans>
                     </ButtonText>
+                    {pending && <ButtonIcon icon={Loader} position="right" />}
                   </Button>
                   <Button
                     label={_(msg`Cancel reactivation and log out`)}
@@ -120,6 +162,21 @@ export function Deactivated() {
                     </ButtonText>
                   </Button>
                 </View>
+
+                {error && (
+                  <View
+                    style={[
+                      a.flex_row,
+                      a.gap_sm,
+                      a.mt_md,
+                      a.p_md,
+                      a.rounded_sm,
+                      t.atoms.bg_contrast_25,
+                    ]}>
+                    <CircleInfo size="md" fill={t.palette.negative_400} />
+                    <Text style={[a.flex_1, a.leading_snug]}>{error}</Text>
+                  </View>
+                )}
               </View>
 
               <View style={[a.pb_3xl]}>
diff --git a/src/screens/Settings/components/DeactivateAccountDialog.tsx b/src/screens/Settings/components/DeactivateAccountDialog.tsx
index 4330ffcaa..99999d068 100644
--- a/src/screens/Settings/components/DeactivateAccountDialog.tsx
+++ b/src/screens/Settings/components/DeactivateAccountDialog.tsx
@@ -3,9 +3,14 @@ import {View} from 'react-native'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
-import {atoms as a, useTheme} from '#/alf'
+import {logger} from '#/logger'
+import {useAgent, useSessionApi} from '#/state/session'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {DialogOuterProps} from '#/components/Dialog'
 import {Divider} from '#/components/Divider'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {Loader} from '#/components/Loader'
 import * as Prompt from '#/components/Prompt'
 import {Text} from '#/components/Typography'
 
@@ -14,11 +19,57 @@ export function DeactivateAccountDialog({
 }: {
   control: DialogOuterProps['control']
 }) {
+  return (
+    <Prompt.Outer control={control}>
+      <DeactivateAccountDialogInner control={control} />
+    </Prompt.Outer>
+  )
+}
+
+function DeactivateAccountDialogInner({
+  control,
+}: {
+  control: DialogOuterProps['control']
+}) {
   const t = useTheme()
+  const {gtMobile} = useBreakpoints()
   const {_} = useLingui()
+  const agent = useAgent()
+  const {logout} = useSessionApi()
+  const [pending, setPending] = React.useState(false)
+  const [error, setError] = React.useState<string | undefined>()
+
+  const handleDeactivate = React.useCallback(async () => {
+    try {
+      setPending(true)
+      await agent.com.atproto.server.deactivateAccount({})
+      control.close(() => {
+        logout('Deactivated')
+      })
+    } catch (e: any) {
+      switch (e.message) {
+        case 'Bad token scope':
+          setError(
+            _(
+              msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`,
+            ),
+          )
+          break
+        default:
+          setError(_(msg`Something went wrong, please try again`))
+          break
+      }
+
+      logger.error(e, {
+        context: 'Failed to deactivate account',
+      })
+    } finally {
+      setPending(false)
+    }
+  }, [agent, control, logout, _, setPending])
 
   return (
-    <Prompt.Outer control={control} testID="confirmModal">
+    <>
       <Prompt.TitleText>{_(msg`Deactivate account`)}</Prompt.TitleText>
       <Prompt.DescriptionText>
         <Trans>
@@ -48,13 +99,32 @@ export function DeactivateAccountDialog({
         <Divider />
       </View>
       <Prompt.Actions>
-        <Prompt.Action
-          cta={_(msg`Yes, deactivate`)}
-          onPress={() => {}}
+        <Button
+          variant="solid"
           color="negative"
-        />
+          size={gtMobile ? 'small' : 'medium'}
+          label={_(msg`Yes, deactivate`)}
+          onPress={handleDeactivate}>
+          <ButtonText>{_(msg`Yes, deactivate`)}</ButtonText>
+          {pending && <ButtonIcon icon={Loader} position="right" />}
+        </Button>
         <Prompt.Cancel />
       </Prompt.Actions>
-    </Prompt.Outer>
+
+      {error && (
+        <View
+          style={[
+            a.flex_row,
+            a.gap_sm,
+            a.mt_md,
+            a.p_md,
+            a.rounded_sm,
+            t.atoms.bg_contrast_25,
+          ]}>
+          <CircleInfo size="md" fill={t.palette.negative_400} />
+          <Text style={[a.flex_1, a.leading_snug]}>{error}</Text>
+        </View>
+      )}
+    </>
   )
 }
diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts
index 7d579d55d..b81cf5962 100644
--- a/src/state/persisted/schema.ts
+++ b/src/state/persisted/schema.ts
@@ -18,9 +18,12 @@ const accountSchema = z.object({
   refreshJwt: z.string().optional(), // optional because it can expire
   accessJwt: z.string().optional(), // optional because it can expire
   signupQueued: z.boolean().optional(),
-  status: z
-    .enum(['active', 'takendown', 'suspended', 'deactivated'])
-    .optional(),
+  active: z.boolean().optional(), // optional for backwards compat
+  /**
+   * Known values: takendown, suspended, deactivated
+   * @see https://github.com/bluesky-social/atproto/blob/5441fbde9ed3b22463e91481ec80cb095643e141/lexicons/com/atproto/server/getSession.json
+   */
+  status: z.string().optional(),
   pdsUrl: z.string().optional(),
 })
 export type PersistedAccount = z.infer<typeof accountSchema>
diff --git a/src/state/session/__tests__/session-test.ts b/src/state/session/__tests__/session-test.ts
index ffffd332e..486604169 100644
--- a/src/state/session/__tests__/session-test.ts
+++ b/src/state/session/__tests__/session-test.ts
@@ -28,6 +28,7 @@ describe('session', () => {
 
     const agent = new BskyAgent({service: 'https://alice.com'})
     agent.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -50,6 +51,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-1",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -88,6 +90,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -116,6 +119,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -138,6 +142,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-1",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -162,6 +167,7 @@ describe('session', () => {
 
     const agent2 = new BskyAgent({service: 'https://bob.com'})
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-1',
@@ -186,6 +192,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "bob-access-jwt-1",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -199,6 +206,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "alice-access-jwt-1",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -223,6 +231,7 @@ describe('session', () => {
 
     const agent3 = new BskyAgent({service: 'https://alice.com'})
     agent3.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-2',
@@ -247,6 +256,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -260,6 +270,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "bob-access-jwt-1",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -284,6 +295,7 @@ describe('session', () => {
 
     const agent4 = new BskyAgent({service: 'https://jay.com'})
     agent4.session = {
+      active: true,
       did: 'jay-did',
       handle: 'jay.test',
       accessJwt: 'jay-access-jwt-1',
@@ -306,6 +318,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "jay-access-jwt-1",
+            "active": true,
             "did": "jay-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -319,6 +332,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -332,6 +346,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "bob-access-jwt-1",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -374,6 +389,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "jay-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -387,6 +403,7 @@ describe('session', () => {
           },
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -400,6 +417,7 @@ describe('session', () => {
           },
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -428,6 +446,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -459,6 +478,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -483,6 +503,7 @@ describe('session', () => {
 
     const agent2 = new BskyAgent({service: 'https://alice.com'})
     agent2.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-2',
@@ -504,6 +525,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -532,6 +554,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -576,6 +599,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -583,6 +607,7 @@ describe('session', () => {
     }
     const agent2 = new BskyAgent({service: 'https://bob.com'})
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-1',
@@ -616,6 +641,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "bob-access-jwt-1",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -653,6 +679,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -669,6 +696,7 @@ describe('session', () => {
     expect(state.currentAgentState.did).toBe('alice-did')
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-2',
@@ -697,6 +725,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": "alice@foo.bar",
             "emailAuthFactor": false,
@@ -720,6 +749,7 @@ describe('session', () => {
     `)
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-3',
@@ -748,6 +778,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-3",
+            "active": true,
             "did": "alice-did",
             "email": "alice@foo.baz",
             "emailAuthFactor": true,
@@ -771,6 +802,7 @@ describe('session', () => {
     `)
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-4',
@@ -799,6 +831,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-4",
+            "active": true,
             "did": "alice-did",
             "email": "alice@foo.baz",
             "emailAuthFactor": false,
@@ -827,6 +860,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -843,6 +877,7 @@ describe('session', () => {
     expect(state.currentAgentState.did).toBe('alice-did')
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-2',
@@ -873,6 +908,7 @@ describe('session', () => {
     expect(lastState === state).toBe(true)
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-3',
@@ -896,6 +932,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -904,6 +941,7 @@ describe('session', () => {
 
     const agent2 = new BskyAgent({service: 'https://bob.com'})
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-1',
@@ -928,6 +966,7 @@ describe('session', () => {
     expect(state.currentAgentState.did).toBe('bob-did')
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice-updated.test',
       accessJwt: 'alice-access-jwt-2',
@@ -956,6 +995,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "bob-access-jwt-1",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -969,6 +1009,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": "alice@foo.bar",
             "emailAuthFactor": false,
@@ -992,6 +1033,7 @@ describe('session', () => {
     `)
 
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob-updated.test',
       accessJwt: 'bob-access-jwt-2',
@@ -1018,6 +1060,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "bob-access-jwt-2",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1031,6 +1074,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "alice-access-jwt-2",
+            "active": true,
             "did": "alice-did",
             "email": "alice@foo.bar",
             "emailAuthFactor": false,
@@ -1083,6 +1127,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -1091,6 +1136,7 @@ describe('session', () => {
 
     const agent2 = new BskyAgent({service: 'https://bob.com'})
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-1',
@@ -1117,6 +1163,7 @@ describe('session', () => {
     expect(state.currentAgentState.did).toBe('bob-did')
 
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-2',
@@ -1142,6 +1189,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -1179,6 +1227,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "alice-access-jwt-1",
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1207,6 +1256,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -1242,6 +1292,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1270,6 +1321,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -1305,6 +1357,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": undefined,
+            "active": true,
             "did": "alice-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1333,6 +1386,7 @@ describe('session', () => {
 
     const agent1 = new BskyAgent({service: 'https://alice.com'})
     agent1.session = {
+      active: true,
       did: 'alice-did',
       handle: 'alice.test',
       accessJwt: 'alice-access-jwt-1',
@@ -1340,6 +1394,7 @@ describe('session', () => {
     }
     const agent2 = new BskyAgent({service: 'https://bob.com'})
     agent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-1',
@@ -1362,6 +1417,7 @@ describe('session', () => {
 
     const anotherTabAgent1 = new BskyAgent({service: 'https://jay.com'})
     anotherTabAgent1.session = {
+      active: true,
       did: 'jay-did',
       handle: 'jay.test',
       accessJwt: 'jay-access-jwt-1',
@@ -1369,6 +1425,7 @@ describe('session', () => {
     }
     const anotherTabAgent2 = new BskyAgent({service: 'https://alice.com'})
     anotherTabAgent2.session = {
+      active: true,
       did: 'bob-did',
       handle: 'bob.test',
       accessJwt: 'bob-access-jwt-2',
@@ -1397,6 +1454,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "jay-access-jwt-1",
+            "active": true,
             "did": "jay-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1410,6 +1468,7 @@ describe('session', () => {
           },
           {
             "accessJwt": "bob-access-jwt-2",
+            "active": true,
             "did": "bob-did",
             "email": undefined,
             "emailAuthFactor": false,
@@ -1434,6 +1493,7 @@ describe('session', () => {
 
     const anotherTabAgent3 = new BskyAgent({service: 'https://clarence.com'})
     anotherTabAgent3.session = {
+      active: true,
       did: 'clarence-did',
       handle: 'clarence.test',
       accessJwt: 'clarence-access-jwt-2',
@@ -1457,6 +1517,7 @@ describe('session', () => {
         "accounts": [
           {
             "accessJwt": "clarence-access-jwt-2",
+            "active": true,
             "did": "clarence-did",
             "email": undefined,
             "emailAuthFactor": false,
diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts
index 2b5e85a49..48f5614bd 100644
--- a/src/state/session/agent.ts
+++ b/src/state/session/agent.ts
@@ -46,6 +46,11 @@ export async function createAgentAndResume(
     emailConfirmed: storedAccount.emailConfirmed,
     handle: storedAccount.handle,
     refreshJwt: storedAccount.refreshJwt ?? '',
+    /**
+     * @see https://github.com/bluesky-social/atproto/blob/c5d36d5ba2a2c2a5c4f366a5621c06a5608e361e/packages/api/src/agent.ts#L188
+     */
+    active: storedAccount.active ?? true,
+    status: storedAccount.status,
   }
   if (isSessionExpired(storedAccount)) {
     await networkRetry(1, () => agent.resumeSession(prevSession))
@@ -235,8 +240,8 @@ export function agentToSessionAccount(
     refreshJwt: agent.session.refreshJwt,
     accessJwt: agent.session.accessJwt,
     signupQueued: isSignupQueued(agent.session.accessJwt),
-    // @ts-expect-error TODO remove when backend is ready
-    status: agent.session.status,
+    active: agent.session.active,
+    status: agent.session.status as SessionAccount['status'],
     pdsUrl: agent.pdsUrl?.toString(),
   }
 }