> For the complete documentation index, see [llms.txt](https://docs.onside.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.onside.io/sdk/core-concepts/authentication.md).

# Authentication & User Account

How OnsideKit handles authentication: explicit and on-demand login, standard and external login flows, reading session state, and logging out.

Most OnsideKit features require an authenticated user. The account determines product availability, pricing, and purchase history. Regular Onside accounts also support saved bank cards; [external accounts](/sdk/core-concepts/external-login.md) have a smaller set of purchase capabilities.

You rarely have to manage login by hand — OnsideKit logs the user in **on demand** when an action needs it.

## How login is triggered

There are two ways the login flow starts.

### Explicit login

Call `Onside.requestLogin(completion:)` — for example, behind a dedicated "Log in" button.

```swift
@MainActor static func requestLogin(
    completion: (@MainActor (Result<Void, OnsideLoginError>) -> Void)? = nil
)
```

```swift
Onside.requestLogin { result in
    switch result {
    case .success:
        // The user is now logged in.
        updateUI()
    case .failure(.loginDiscarded):
        // The user dismissed the login screen.
        break
    }
}
```

`OnsideLoginError` has a single case, `.loginDiscarded`. It is delivered when the user cancels the flow — and also when the login screen could not be presented at all, because no active `UIWindowScene` was available. Treat it as "this login attempt did not complete" rather than strictly "user said no". Read the storefront to determine the current session state. The `completion` is optional — call `Onside.requestLogin()` to log in without handling the result.

### Implicit (on-demand) login

You usually don't need to call `requestLogin` first. When the user is not authenticated and performs an action that needs an account, OnsideKit presents the login flow automatically and then resumes the original action.

On-demand login is triggered by:

* Starting a purchase — `Onside.defaultPaymentQueue().add(_:completion:)`
* Restoring purchases — `Onside.defaultPaymentQueue().restoreCompletedTransactions(completion:)`
* Presenting the payment methods manager — `Onside.presentPaymentMethodsManager(completion:)`

If the user dismisses the login screen, the corresponding call reports `.loginDiscarded` through its completion handler.

{% hint style="info" %}
Not every account-related call logs the user in. `Onside.makeSignedInAppsHistoryRequest()` does **not** present login — it returns `.failure(.notLoggedIn)` when there is no active session. See [Signed In-App Purchase History](/sdk/purchase-validation/signed-in-apps-history.md).
{% endhint %}

## Choosing a login flow

If `Onside.externalLoginDelegate` is assigned, the SDK first requests your app’s account identifier and attempts external login. A missing identifier or session-creation failure falls back to the regular login method. See [External Login](/sdk/core-concepts/external-login.md).

Without an external delegate, or after that fallback, `OnsideDelegate.onsideLoginMethod()` selects the regular flow:

* **App-to-app login (preferred).** If the Onside store app is installed **and** you set `Onside.callbackScheme`, OnsideKit hands off to the Onside app for a seamless login and returns the user to your app. This requires the URL-scheme setup and `Onside.handle(url:)` forwarding from [Initializing the SDK](/sdk/getting-started/initialization.md).
* **In-SDK login (fallback).** Otherwise OnsideKit presents its own phone-number login screen. This is used when the Onside app isn't installed, when `callbackScheme` isn't set, or when you force it via the delegate (see below).

{% hint style="info" %}
Implement `OnsideDelegate.onsideLoginMethod()` and return `.local` to use the in-SDK phone login instead of the app-to-app handoff. The default is `.auto`. An assigned external delegate takes priority over either value; `.local` does not disable external login. See [The Onside Delegate](/sdk/customization/delegate.md).
{% endhint %}

## Reading the session state

The session is represented by the **storefront** on the payment queue. A non-`nil` storefront means there is an authenticated session.

```swift
struct OnsideStorefront {
    var id: String
    var countryCode: String   // the user's region, e.g. "US"
}
```

### Check the current status

```swift
func updateUI() {
    let queue = Onside.defaultPaymentQueue()
    let isLoggedIn = queue.storefront != nil

    loginButton.isHidden = isLoggedIn

    if let region = queue.storefront?.countryCode {
        print("Authenticated. Storefront region: \(region)")
    }
}
```

### Observe changes

To react to login and logout in real time, add an observer conforming to `OnsidePaymentTransactionObserver`. OnsideKit calls `onsidePaymentQueueDidChangeStorefront(_:)` whenever the storefront changes (including `nil` → value on login and value → `nil` on logout).

```swift
import OnsideKit

final class SessionObserver: OnsidePaymentTransactionObserver {

    init() {
        Onside.defaultPaymentQueue().add(observer: self)
    }

    deinit {
        Onside.defaultPaymentQueue().remove(observer: self)
    }

    // Required by the protocol.
    func onsidePaymentQueue(
        _ queue: OnsidePaymentQueue,
        updatedTransactions: [OnsidePaymentTransaction]
    ) {
        // Handle transactions — see "The Payment Queue & Transactions".
    }

    func onsidePaymentQueueDidChangeStorefront(_ queue: OnsidePaymentQueue) {
        print("Storefront changed. Logged in: \(queue.storefront != nil)")
    }
}
```

{% hint style="warning" %}
Observers are held **weakly** — you must keep a strong reference to your observer, and you should `remove(observer:)` it when it goes away. See [Threading & Object Lifetime](/sdk/core-concepts/threading-and-retention.md).
{% endhint %}

## Logging out

`Onside.logout()` ends the current session and clears the user's local authentication state (tokens and cached profile).

```swift
Onside.logout()
```

After logout, `Onside.defaultPaymentQueue().storefront` becomes `nil`, and the next action that requires an account triggers the login flow again. Logout does not remove `Onside.externalLoginDelegate`; if it still returns an account identifier, the next login can create an external session again.

{% hint style="info" %}
OnsideKit may also log the user out automatically if the server invalidates the session (for example, the cached profile can no longer be refreshed). Observe the storefront, as shown above, to keep your UI in sync.
{% endhint %}

## Authentication, region, and pricing

The signed-in account also determines the user's region, which drives product availability and pricing. You can fetch products before login using a best-guess region, then re-fetch once the storefront is known. See [Regions & Storefronts](/sdk/products-and-subscriptions/regions-and-storefronts.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.onside.io/sdk/core-concepts/authentication.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
