Single Sign On (SSO) with Mobile SDKs

Descope supports SSO as one of the authentication methods for your end-users. When using SSO, the SSO configuration can be different for each tenant. Please refer to the article in the manage section for the configuration of SSO for each tenant.

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
// 1. Within Android Studio, go to File > Project Structure > Dependencies > Add Dependency > 1 Library Dependency
// 2. Search for the dependency: "com.descope"
// 3. Configure your desired dependency rules
// 4. Click "Ok"
// 1. From your Flutter project directory root, install the Descope SDK by running: flutter pub add descope
// 2. Or, add Descope to your pubspec.yml by including this line: descope: ^0.9.0
// View the package on pub.dev: https://pub.dev/packages/descope
// 1. From your React Native project directory root, install the Descope SDK by running: npm i @descope/react-native-sdk
// View the package: https://github.com/descope/descope-react-native

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)
}
import android.app.Application
import com.descope.Descope

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        try {
            Descope.setup(this, projectId = "__ProjectID__") {
              // Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
              baseUrl = "https://auth.app.example.com"
              // Enable the logger
              if (BuildConfig.DEBUG) {
                logger = DescopeLogger()
              }
            }
        } catch (e: Exception) {
            Log.e("ERROR", e.stackTraceToString())
        }
    }
}
import 'package:descope/descope.dart';

// Where your application state is being created
Descope.setup('<Your-Project-Id>', (config) {
  // Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
  config.baseUrl = 'https://auth.app.example.com';
});
await Descope.sessionManager.loadSession();
import { AuthProvider } from '@descope/react-native-sdk'

const AppRoot = () => {
  return (
    <AuthProvider
      projectId="__ProjectID__"
      // Optional: Only set baseURL if using a custom domain with Descope and managing token response with cookies
      baseUrl = "https://auth.app.example.com"
    >
      <App />
    </AuthProvider>
  )
}

Start SSO

To initiate the SSO process, call the SSO initiation function after the user clicks the login button. This function automatically opens the SSO Identity Provider's 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)
}
// Args:
//   emailOrTenantName: ID of the tenant that the user is authenticating to. The tenant ID is assigned to tenant at the time of creation.
val emailOrTenantName = "email@company.com"
//   redirectURL: 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'.
val redirectURL = "exampleauthschema://auth.company.com/handle-sso"
//  options: optional options to get attributes like custom claims, stepup, mfa, and revoke sessions in response
val options = listOf(
    SignInOptions.CustomClaims(mapOf("cc1" to "yes", "cc2" to true)),
    SignInOptions.StepUp(session.refreshJwt),
    SignInOptions.Mfa(session.refreshJwt),
    SignInOptions.RevokeOtherSessions
)

try {
    val authURL = Descope.sso.start(emailOrTenantName, redirectURL, options)
    val uri = Uri.parse(authURL)

    if (uri != null) {
        println("Successfully initiated SSO Authentication")
    } else {
        println("Failed to initiate SSO Authentication")
    }
} catch (exception: Exception) {
    println("Failed to initiate SSO Authentication")
    println(exception)
}
// 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"
// options: Optional options to get custom claims in response
    const options = SignInOptions(customClaims: {'name': '{{user.name}}'});
 // Choose which tenant to log into
// If configured globally, the return URL is optional. If provided however, it will be used
// instead of any global configuration.
  final authUrl = await Descope.sso.start(
      emailOrTenantId: 'my-tenant-ID',
      redirectUrl: 'exampleauthschema://my-app.com/handle-sso', options: options);
// Args:
//   tenant_name_id_or_email: ID of the tenant that the user is authenticating to. The tenant ID is assigned to tenant at the time of creation.
const tenant_name_id_or_email = "xxxx"
//   redirectURL: 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'.
const redirectURL = "https://auth.company.com/token_exchange"
//    loginOptions (LoginOptions): this allows you to configure behavior during the authentication process.
const loginOptions = {
      "stepup": false,
      "mfa": false,
      "customClaims": {"claim": "Value1"},
      "templateOptions": {"option": "Value1"}
    }
//    refreshToken (optional): the user's current refresh token in the event of stepup/mfa
 
const descopeSdk = useDescope();
const resp = await descopeSdk.saml.start(tenant_name_id_or_email, redirectURL, loginOptions);
if (!resp.ok) {
  console.log("Failed to start sso auth")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  const url = resp.data.url
  console.log("Successfully started sso auth. URL: " + url)
}

SSO Exchange Code

After successful authentication with your IdP the user is redirected to the redirect_url that you provide in the sso start function above. Your application should extract the code from the redirect_url and perform token exchange 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)
}
// Args:
//   authURL: the authURL generated from the Start SSO
val code = "xxxxxx" // Code from authURL

try {
  if (code != null) {
    val descopeSession = Descope.oauth.exchange(code)
      println("Successfully completed OAuth Authentication")
      println(descopeSession)
  }
} catch (exception: Exception) {
    println("Failed to complete SSO Authentication")
    println(exception)
}
// Args:
//   authURL: the authURL generated from the Start SSO
const authURL = "xxxxx";

// Redirect the user to the returned URL to start the OAuth redirect chain
  final result = await FlutterWebAuth.authenticate(
      url: authUrl, callbackUrlScheme: 'exampleauthschema');
// Extract the returned code
  final code = Uri.parse(result).queryParameters['code'];
// Exchange code for an authentication response
  final authResponse = await Descope.sso.exchange(code: code!);
// Args:
//   code: code extracted from the url after user is redirected to redirectURL. The code is in the url as a query parameter "code" of the page.
const code = "xxxxx"
 
const descopeSdk = useDescope();
const resp = await descopeSdk.saml.exchange(code);
if (!resp.ok) {
  console.log("Failed to verify sso code")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully verified sso code.")
}

Session Validation

The final step of completing the authentication with Descope is to validate the user session. Descope provides rich session management capabilities, including configurable session timeouts and logout functions. You can find the details and sample code for client session validation here.

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