954268a4b6
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import type * as AgeRange from 'expo-age-range'
|
|
import {type computeAgeAssuranceRegionAccess} from '@atproto/api'
|
|
|
|
import {logger} from '#/ageAssurance/logger'
|
|
|
|
/**
|
|
* Native on-device age signals, keyed by the region (a `country[-region]`
|
|
* string, see `createRegionKey`) they were captured in. We keep one entry per
|
|
* region so multiple regions with differing criteria can each retain their own
|
|
* grant.
|
|
*
|
|
* Device assurance can't be verified server-side (the OS gives us no signed
|
|
* attestation, only age bounds), so we persist it client-side only and bind it
|
|
* to its capture region via the key. A grant captured in TX is only ever read
|
|
* back for TX — it can't silently unlock another region. See
|
|
* `getAgeAssuranceDataFromDeviceSignals`.
|
|
*/
|
|
export type AgeAssuranceDeviceSignals = {
|
|
[regionKey: string]: AgeRange.AgeRangeResponse
|
|
}
|
|
|
|
export enum AgeAssuranceAccess {
|
|
Unknown = 'unknown',
|
|
None = 'none',
|
|
Safe = 'safe',
|
|
Full = 'full',
|
|
}
|
|
|
|
export enum AgeAssuranceStatus {
|
|
Unknown = 'unknown',
|
|
Pending = 'pending',
|
|
Assured = 'assured',
|
|
Blocked = 'blocked',
|
|
}
|
|
|
|
export type AgeAssuranceMetadata = Parameters<
|
|
typeof computeAgeAssuranceRegionAccess
|
|
>[1] & {
|
|
birthdate: string | undefined
|
|
}
|
|
|
|
export type AgeAssuranceState = {
|
|
lastInitiatedAt?: string
|
|
status: AgeAssuranceStatus
|
|
access: AgeAssuranceAccess
|
|
error?: 'config' // maybe other specific cases in the future
|
|
}
|
|
|
|
export type AgeAssuranceFlags = {
|
|
isAgeRestricted: boolean
|
|
adultContentDisabled: boolean
|
|
chatDisabled: boolean
|
|
groupChatDisabled: boolean
|
|
hasDeclaredAge: boolean
|
|
isDeclaredUnderAdultAge: boolean
|
|
isOverRegionMinAccessAge: boolean
|
|
isOverAppMinAccessAge: boolean
|
|
allowsDeviceVerification: boolean
|
|
hasSharedDeviceSignals: boolean
|
|
}
|
|
|
|
export function parseStatusFromString(raw: string) {
|
|
switch (raw) {
|
|
case 'unknown':
|
|
return AgeAssuranceStatus.Unknown
|
|
case 'pending':
|
|
return AgeAssuranceStatus.Pending
|
|
case 'assured':
|
|
return AgeAssuranceStatus.Assured
|
|
case 'blocked':
|
|
return AgeAssuranceStatus.Blocked
|
|
default:
|
|
logger.error(`parseStatusFromString: unknown status value: ${raw}`)
|
|
return AgeAssuranceStatus.Unknown
|
|
}
|
|
}
|
|
|
|
export function parseAccessFromString(raw: string) {
|
|
switch (raw) {
|
|
case 'unknown':
|
|
return AgeAssuranceAccess.Unknown
|
|
case 'none':
|
|
return AgeAssuranceAccess.None
|
|
case 'safe':
|
|
return AgeAssuranceAccess.Safe
|
|
case 'full':
|
|
return AgeAssuranceAccess.Full
|
|
default:
|
|
logger.error(`parseAccessFromString: unknown access value: ${raw}`)
|
|
return AgeAssuranceAccess.Full
|
|
}
|
|
}
|