Single Sign-On (SSO) with Mobile SDKs

Use the mobile SDK to run SSO natively in an iOS, Android, Flutter, or React Native app. SSO is configured per tenant, so each customer's SAML or OIDC connection can point at a different identity provider. See tenant management for how to set that up.

Note

Before the code below will work, enable SSO at the project level and configure a SAML or OIDC connection for at least one tenant. For the full walkthrough, see Getting Started with SSO, or test without your own IdP.

Client SDK

Install SDK

// 1. Within XCode, go to File > Add Packages
// 2. Search for the URL of the git repo: https://github.com/descope/descope-swift
// 3. Configure your desired dependency rule
// 4. Click Add Package

Import and Initialize SDK

import DescopeKit
import AuthenticationServices

do {
    Descope.setup(projectId: "__ProjectID__") { config in
      // Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
      config.baseURL = "https://auth.app.example.com"
    }
    print("Successfully initialized Descope")
} catch {
    print("Failed to initialize Descope")
    print(error)
}

Start SSO

When the user taps sign in with SSO, call the start function. It opens the tenant's identity provider login screen in a browser webview.

// Args:
//   emailOrTenantName: ID of the tenant that the user is authenticating to. The tenant ID is assigned to tenant at the time of creation.
let emailOrTenantName = "email@company.com"
//   redirect_url: URL to return to after successful authentication with the SSO identity provider. You need to implement this page to access the token and finish oauth process (token exchange). The token arrives as a query parameter named 'code'.
let redirectURL = "exampleauthschema://auth.company.com/handle-sso"

guard let session = Descope.sessionManager.session else { return }
var signInOptions: [SignInOptions] = [
    .customClaims(["name": "{{user.name}}"]),
    .mfa(refreshJwt: session.refreshJwt),
    .stepup(refreshJwt: session.refreshJwt)
]

do {
  let authURL = try await Descope.sso.start(emailOrTenantName: emailOrTenantName, redirectURL: redirectURL, options: signInOptions)
  guard let authURL = URL(string: authURL) else { return }
  print("Successfully initiated SSO Authentication")
} catch {
  print("Failed to initiate SSO Authentication")
  print(error)
}

SSO Exchange Code

After the user authenticates, the IdP sends them back to the redirect_url you passed to the start function, with the code in the query string. Pull the code off the URL and exchange it as shown below.

// Args:
//   authURL: the authURL generated from the Start SSO
let authURL = "xxxxx"

do {
  let session = ASWebAuthenticationSession(
    url: authURL,
    callbackURLScheme: "exampleauthschema") { callbackURL, error in
      guard let url = callbackURL else {return}
      let component = URLComponents(url: url, resolvingAgainstBaseURL: false)
      guard let code = component?.queryItems?.first(where: {$0.name == "code"})?.value else { return }
      print(code)

      // Exchange code for session
      Task {
        do {
          let descopeSession = try await Descope.sso.exchange(code: code)
          print("Successfully Completed SSO Authentication")
          print(descopeSession as Any)
        } catch {
          print("Failed to Complete SSO Authentication")
          print(error)
      }
    }
  }
  session.presentationContextProvider = self
  session.prefersEphemeralWebBrowserSession = true
  session.start()
} catch {
  print("Failed to Complete SSO Authentication")
  print(error)
}

Session Validation

The last step is validating the user's session. Descope handles session management for you, including configurable timeouts and logout. For the details and sample code, see mobile session validation.

Checkpoint

Your application is now integrated with Descope. Please test with sign-up or sign-in use case.

Need help?
Was this helpful?

On this page