Session
Read who the SDK is acting for, follow the session as it changes, and sign out.
Preview
This page describes a pre-release version of the SDK. Names, versions and APIs on this page can change before the release.
A session is the identity the SDK acts for on this device, together with the services that run for it. Every request the SDK sends to Folio belongs to the current session, and the data the SDK keeps on the device, such as the vault, belongs to the identity of that session.
SessionStore is the store that reports the session to your app. It tells you which identity is
current, whether the services for it are running, and it signs the person out. To sign in, use
AuthStore.
Guests and accounts
The SDK always acts for an identity. There are two kinds:
- Guest (
Transient): an identity that Folio creates for this device without asking the person anything. The SDK creates it the first time the runtime starts on a device, and again after a sign-out. A guest has a user id and no email address. - Account (
Authenticated): an identity the person signed in to with the authentication ceremony. It has a user id and an email address.
A sign-in can also end on a guest identity: when the account that Folio signs the person in to is
itself a guest account, the identity stays Transient.
When a guest signs in to an account, the SDK carries the vault records of the guest over to the account. The data of the guest stays on the device until that transfer has finished; if the app stops before then, the SDK resumes the transfer the next time the account's services start.
FolioSdk.create restores the session that was saved on the device, or creates a guest session
when there is none, before it returns. When it cannot, create fails with a
SessionStartError. A runtime that exists therefore always has a session. See
Runtime.
Mount the store
SessionStore takes no init value. Mount it on the runtime, read its state and close it when your
app no longer needs it.
let session = try SessionStore.mount(runtime: sdk)
let cancellable = session.$state
.map(\.identity)
.sink { identity in
switch identity {
case .unresolved:
showSignedOut()
case let .transient(userId):
showGuest(userId)
case let .authenticated(userId, email):
showAccount(userId, email)
}
}On iOS SessionStore is an ObservableObject that publishes its state as @Published state,
updated on the main actor; on Android state is a StateFlow<SessionUiModel>; on the web read
state and register a listener with subscribe, which returns the function that removes it.
mount takes the runtime, a FolioSdk. Close the store with close() when you no longer need it.
To read the states one by one instead, see Subscriptions.
React
On the web, useSessionStore from @folio/sdk/stores mounts SessionStore for the component that
calls it. It needs a FolioSDKProvider from @folio/sdk/provider above that component.
import { FolioSDKProvider } from '@folio/sdk/provider';
import { useSessionStore } from '@folio/sdk/stores';
import { type FolioSdk, SessionAction } from '@folio/sdk';
function App({ runtime }: { runtime: FolioSdk }) {
return (
<FolioSDKProvider runtime={runtime}>
<Profile />
</FolioSDKProvider>
);
}
function Profile() {
const { store, state } = useSessionStore();
if (store === undefined || state === undefined) return null;
if (state.identity.type !== 'AUTHENTICATED') return <p>Guest</p>;
return (
<button disabled={state.signingOut} onClick={() => store.dispatch(SessionAction.signOut)}>
Sign out {state.identity.value.email}
</button>
);
}useSessionStore returns { store, state }. Both are undefined until the store is mounted after
the first render. The hook re-renders the component on every state change, and closes the store when
the component unmounts or the runtime changes.
Mounting the store does not start or change a session. The store follows the session the runtime
already has and reports every change to it, whichever store or event caused the change: a sign-in
through AuthStore, a sign-out, or a session that Folio ended.
State
The state of the store is SessionUiModel.
| Field | Type | Meaning |
|---|---|---|
identity | RuntimeIdentity | The identity the session acts for. |
status | SessionStatus | Whether the services for that identity are running. |
notice | SessionNoticeReason? | Why the account session ended without a sign-out from your app. Empty otherwise. |
deviceId | String? | The id the Folio backend gave this installation. Empty until the first session start. |
signingOut | Bool / Boolean / boolean | true from the moment you dispatch signOut until the sign-out has finished or failed. |
error | SessionStartError? | Why the last sign-out failed. Cleared when you dispatch signOut again. |
Until the store has read the session for the first time, its state holds the defaults: identity
Unresolved, status Device, no notice and no device id.
RuntimeIdentity
| Case | Fields | Meaning |
|---|---|---|
Unresolved | none | No identity. The SDK has no session for anyone, for example during a sign-out, or after one when no new guest session could be created. |
Transient | userId | A guest. |
Authenticated | userId, email | A signed-in account. email is empty when no email address is known for the account: after a recovery started with a phone number, or when Folio returns none for a sign-in. |
| Platform | Spelling |
|---|---|
| Swift | .unresolved, .transient(userId:), .authenticated(userId:email:) |
| Kotlin | RuntimeIdentity.Unresolved, RuntimeIdentity.Transient(userId), RuntimeIdentity.Authenticated(userId, email) |
| TypeScript | { type: 'UNRESOLVED' }, { type: 'TRANSIENT', value: { userId } }, { type: 'AUTHENTICATED', value: { userId, email } } |
SessionStatus
The status tells you whether the services that belong to the identity, such as the vault, are running.
| Value | Swift | Kotlin | TypeScript | Meaning |
|---|---|---|---|---|
Device | .device | SessionStatus.DEVICE | 'DEVICE' | No identity is active. Nothing that belongs to a user is running. |
Activating | .activating | SessionStatus.ACTIVATING | 'ACTIVATING' | The SDK is starting the services for a signed-in account. |
Active | .active | SessionStatus.ACTIVE | 'ACTIVE' | The services for the current identity, guest or account, are running. |
Deactivating | .deactivating | SessionStatus.DEACTIVATING | 'DEACTIVATING' | The SDK is stopping the services of the previous identity. |
On iOS and Android SessionStatus is an enum. On the web it is a string, and the constant object
SessionStatus holds the same strings, for example SessionStatus.ACTIVE is 'ACTIVE'.
The status moves through these values when the identity changes:
- To an account:
Activating, thenActiveonce the account's services have started. - To a guest: straight to
Active. - To no identity:
Deactivating, thenDevice.
If the services for the new identity cannot start, the SDK goes back to the previous identity with
status Device: the store reports that identity again, with its services stopped.
SessionNoticeReason
notice tells your app that the account session has ended although your app did not sign out, so
that it can tell the person. It has one case:
| Case | Swift | Kotlin | TypeScript | Meaning |
|---|---|---|---|---|
SessionEnded | .sessionEnded | SessionNoticeReason.SessionEnded | { type: 'SESSION_ENDED' } | Folio ended the account session. See Expired sessions. |
On the web the constant SessionNoticeReason.sessionEnded holds the same value. notice is
cleared when the identity becomes a signed-in account again and when you dispatch DismissNotice.
Actions
SessionAction has two actions.
| Action | Swift | Kotlin | TypeScript | What it does |
|---|---|---|---|---|
SignOut | .signOut | SessionAction.SignOut | SessionAction.signOut | Signs out. See Sign out. |
DismissNotice | .dismissNotice | SessionAction.DismissNotice | SessionAction.dismissNotice | Clears notice. Does nothing when no notice is shown. |
try session.dispatch(action: .signOut)Sign out
SignOut ends the current session, whether it belongs to an account or to a guest, and leaves the
device with a new guest session. While it runs, signingOut is true. A second SignOut while
one is running does nothing.
The SDK signs out in this order:
- It stops the services of the current identity, such as the vault.
- It asks Folio to end the session. When Folio cannot be reached, the SDK goes on with the sign-out on the device.
- It deletes the session tokens from the device.
- It deletes the saved session of the previous identity from the device.
- It forgets the identity:
identitybecomesUnresolvedandstatusgoes throughDeactivatingtoDevice. - It creates a new guest session:
identitybecomesTransientwith a new user id andstatusbecomesActive.
When the sign-out has finished, signingOut is false and error is empty. When a step fails,
signingOut is false and error holds the reason. For example, when the device is offline,
step 6 fails with a Network error: the person is signed out, identity stays Unresolved, and
there is no guest session.
The sign-out ends the session. It does not delete the account.
Expired sessions
The SDK renews the session tokens by itself while the app runs. When Folio has ended the session and it can no longer be renewed, the runtime itself ends it; no store has to be mounted for that.
- A guest gets a new session and keeps its vault.
- An account ends as if your app had signed out: the device is left with a new guest session,
and
noticebecomesSessionEnded, unless the account was deleted. To continue with the account, sign the person in again.
A mounted AuthStore returns to Idle and drops the sign-in that was in
progress.
Errors
SessionStartError
SessionStartError is the error of FolioSdk.create and the type of SessionUiModel.error.
| Case | Fields | Meaning |
|---|---|---|
Backend | status, code, message | Folio rejected the request. status is the HTTP status, code the Folio error code. |
Network | message | Folio could not be reached. |
Scope | message | The services of the new identity could not be started, or the runtime was closed while the session changed. |
Core | error | The SDK failed on the device, for example because its storage could not be read or written. error is a CoreError. |
| Platform | Spelling |
|---|---|
| Swift | .backend(status:code:message:), .network(message:), .scope(message:), .core(error:) |
| Kotlin | SessionStartError.Backend(status, code, message), SessionStartError.Network(message), SessionStartError.Scope(message), SessionStartError.Core(error) |
| TypeScript | { type: 'BACKEND', value: { status, code, message } }, { type: 'NETWORK', value: { message } }, { type: 'SCOPE', value: { message } }, { type: 'CORE', value: { error } } |
status is a UInt16 on iOS, an Int on Android and a number on the web. code and message
are strings.
dispatch itself fails with a CoreError when the store cannot take the action,
for example after the runtime was shut down. For what a closed store throws, see
Store errors.
See also
- Authentication: sign a person in, recover an account, verify a sensitive operation.
- Runtime: create the runtime that holds the session.
- Storage: what the SDK keeps on the device.