Overview
TokenManager is a thread-safe actor that manages Strava OAuth tokens using the iOS Keychain. It provides secure storage, retrieval, and deletion of authentication tokens with support for app groups and keychain access control.
Initialization
- Service:
com.mattbolanos.stratiles.tokens - Account:
strava - Synchronizable:
false(tokens are not synced via iCloud)
Methods
loadToken
Retrieves the stored Strava token from the keychain.StravaToken? - The stored token, or nil if no token exists or decoding fails
Implementation Details:
- Uses
kSecClassGenericPasswordfor storage - Queries keychain with
kSecMatchLimitOneto retrieve single item - Automatically decodes JSON data to
StravaToken - Returns
nilsilently if keychain access fails or token doesn’t exist - Respects app group access if
AppIdentifierPrefixis configured
saveToken
Securely stores a Strava token in the keychain.The token to store, containing access token, refresh token, and expiration time
TokenManagerError.keychainOperationFailed(OSStatus) if keychain operations fail
Implementation Details:
- First attempts to update existing token with
SecItemUpdate - If no token exists (
errSecItemNotFound), adds new item withSecItemAdd - Uses
kSecAttrAccessibleAfterFirstUnlockfor accessibility - Encodes token as JSON before storing
- Supports app group sharing via access group
clearToken
Deletes the stored token from the keychain.- Calls
SecItemDeleteto remove the token - Does not throw errors - silently succeeds even if no token exists
- Useful for logout flows or when resetting authentication
hasRefreshToken
Checks if a valid refresh token is stored.true if a token exists and has a non-empty refresh token, false otherwise
Implementation Details:
- Loads the token using
loadToken() - Returns
falseif no token exists - Checks that
refreshTokenproperty is not empty
Error Handling
TokenManagerError
keychainOperationFailed(OSStatus)- A keychain operation failed with the given status code
errSecSuccess(0) - Operation succeedederrSecItemNotFound(-25300) - Item not found in keychainerrSecDuplicateItem(-25299) - Item already existserrSecAuthFailed(-25293) - Authentication failed
Keychain Configuration
Access Group
The manager automatically configures keychain access groups for app group sharing:AppIdentifierPrefixmust be set in Info.plist for app group support- Format:
{prefix}com.mattbolanos.stratiles - Allows sharing tokens between app and extensions
Keychain Query Structure
All keychain operations use this base query:Security Features
- Thread-safe: Actor isolation ensures serial access to keychain operations
- Local-only: Tokens are not synced via iCloud Keychain (
kSecAttrSynchronizable: false) - After first unlock: Tokens accessible after device is unlocked once (
kSecAttrAccessibleAfterFirstUnlock) - App group support: Tokens can be shared between app and extensions
- Atomic updates: Uses
SecItemUpdatefirst, thenSecItemAddto avoid race conditions