# Authentication & User Account

Most OnsideKit features require an authenticated user. A signed-in account securely stores payment methods and purchase history, and determines the correct product availability and pricing for the user's region.

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`, delivered when the user cancels. 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 %}

## The two login flows

Whichever way login starts, OnsideKit uses one of two flows:

* **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" %}
You can force the in-SDK flow with `OnsideDelegate.onsideShouldForceLocalLoginMethods()`. Returning `true` always uses OnsideKit's own login screen and skips the app-to-app handoff. It is purely a UI-routing switch — it does not change which credentials are accepted. 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.

{% 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: Querying This Documentation

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

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

The question should be specific, self-contained, and written in natural language.
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.
