# Login with OAuth

OAuth lets users sign in with a social provider — Google, Discord, Twitter, Facebook, and others. On iOS the flow is **browser-based**: you open the provider's consent page in Safari, the user authenticates, and the provider redirects back into your app via a deep link carrying the credentials.

The flow has three parts:

1. **Start** — `initOAuth` returns a URL. Open it; the system browser shows the provider's login.
2. **Redirect** — after consent, the provider sends the user back to your app's deep link with `token` and `user_id` query parameters.
3. **Store** — pass those values to `storeCredentials` to finalize the Openfort session.

:::note
**Sign in with Apple** uses a different, native flow (no browser) and is documented separately — see [Sign in with Apple](/docs/products/embedded-wallet/swift/auth/sign-in-with-apple).
:::

## Prerequisites

* Enable each OAuth provider in the **Openfort Dashboard → Auth Providers** (see [social login configuration](/docs/configuration/social-login)).
* Register a **URL scheme** for your app (Xcode → target → Info → URL Types) so the redirect can deep-link back. The examples below use `myapp://login`.

## Implementation

```swift
import SwiftUI
import OpenfortSwift

@MainActor
final class OAuthModel: ObservableObject {
    @Published var status = ""

    /// Step 1 — start the flow and open the provider's consent page.
    func signIn(with provider: OFOAuthProvider) async {
        do {
            let result = try await OFSDK.shared.initOAuth(
                params: OFInitOAuthParams(
                    provider: provider.rawValue,
                    options: ["redirectTo": AnyCodable("myapp://login")]
                )
            )
            guard let urlString = result?.url, let url = URL(string: urlString) else {
                status = "No OAuth URL returned."
                return
            }
            await UIApplication.shared.open(url)
        } catch {
            status = "Couldn't start OAuth: \(error.localizedDescription)"
        }
    }

    /// Step 3 — finalize the session with the credentials from the redirect.
    func handleRedirect(_ url: URL) async {
        guard
            url.host == "login",
            let comps = URLComponents(url: url, resolvingAgainstBaseURL: false),
            let token = comps.queryItems?.first(where: { $0.name == "token" })?.value,
            let userId = comps.queryItems?.first(where: { $0.name == "user_id" })?.value,
            !token.isEmpty, !userId.isEmpty
        else { return }

        do {
            try await OFSDK.shared.storeCredentials(
                params: OFStoreCredentialsParams(token: token, userId: userId)
            )
            status = "Signed in."
        } catch {
            status = "Couldn't store credentials: \(error.localizedDescription)"
        }
    }
}
```

Wire up the start and the redirect in your view:

```swift
struct LoginView: View {
    @StateObject private var model = OAuthModel()

    var body: some View {
        VStack(spacing: 12) {
            Button("Sign in with Google") {
                Task { await model.signIn(with: .google) }
            }
            Button("Sign in with Discord") {
                Task { await model.signIn(with: .discord) }
            }
            Text(model.status).font(.footnote)
        }
        // Step 2 — the provider redirects here with ?token=...&user_id=...
        .onOpenURL { url in
            Task { await model.handleRedirect(url) }
        }
    }
}
```

:::tip
The `redirectTo` value you pass to `initOAuth` **must** match the URL scheme you registered in Xcode and the redirect host you check in `handleRedirect` (`url.host == "login"` here). A mismatch is the most common reason the user gets stuck in the browser after consenting.
:::

## Available OAuth providers

```swift
public enum OFOAuthProvider: String {
    case google      = "google"
    case twitter     = "twitter"
    case apple       = "apple"
    case facebook    = "facebook"
    case discord     = "discord"
    case epicGames   = "epic_games"
    case line        = "line"
}
```

## Parameters

```swift
public struct OFInitOAuthParams: OFCodableSendable {
    public let provider: String                 // e.g. OFOAuthProvider.google.rawValue
    public let options: [String: AnyCodable]?   // pass ["redirectTo": AnyCodable("myapp://login")]
}

public struct OFStoreCredentialsParams: OFCodableSendable {
    public let token: String
    public let userId: String
}
```

## Returns

`initOAuth` returns an `OFInitOAuthResponse` whose `url` you open in the browser:

```swift
public struct OFInitOAuthResponse: OFCodableSendable {
    public let url: String? // open this in the system browser
    public let key: String?
}
```

## Next steps

* [Configure the embedded wallet](/docs/products/embedded-wallet/swift/wallet/recovery) after login
* [User session & access tokens](/docs/products/embedded-wallet/swift/auth/user-session)
* Prefer your own identity provider? See [third-party authentication](/docs/products/embedded-wallet/swift/auth/third-party)
