> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.generaltranslation.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Login to Your iOS or macOS Application

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              processedNode = processedNode.replace(new RegExp(key, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

## Get Started

<Steps>
  <Step title="Create a new project" stepNumber={1}>
    Create a new iOS or macOS project for this quickstart.

    **In Xcode:**

    1. **File** → **New** → **Project** (or **⌘+Shift+N**)
    2. Select either:
       * **iOS** tab → **App** template
       * **macOS** tab → **App** template
    3. Configure your project:
       * **Product Name**: `Auth0-Sample`
       * **Interface**: SwiftUI
       * **Language**: Swift
       * **Use Core Data**: Unchecked
       * **Include Tests**: Checked (recommended)
    4. Choose a location and click **Create**

    <Tip>
      This creates a standard app with SwiftUI and Swift Package Manager support, perfect for Auth0 integration.
    </Tip>
  </Step>

  <Step title="Add the Auth0 SDK" stepNumber={2}>
    Add the Auth0 SDK to your project using your preferred package manager.

    <Tabs>
      <Tab title="Swift Package Manager">
        **In Xcode:**

        1. **File** → **Add Package Dependencies...** (or **⌘+Shift+K**)
        2. Enter the Auth0 SDK URL:
           ```
           https://github.com/auth0/Auth0.swift
           ```
        3. **Add Package** → Select your app target → **Add Package**
      </Tab>

      <Tab title="CocoaPods">
        1. Create a `Podfile` in your project directory:
           ```ruby Podfile theme={null}
           platform :ios, '14.0' # Or platform :osx, '11.0' for macOS
           use_frameworks!

           target 'YourApp' do
             pod 'Auth0', '~> 2.0'
           end
           ```
        2. Install the dependencies:
           ```bash theme={null}
           pod install
           ```
        3. Open the generated `.xcworkspace` file (not `.xcodeproj`)
      </Tab>

      <Tab title="Carthage">
        1. Create a `Cartfile` in your project directory:
           ```text Cartfile theme={null}
           github "auth0/Auth0.swift" ~> 2.0
           ```
        2. Run Carthage:
           ```bash theme={null}
           carthage update --platform iOS --use-xcframeworks
           ```
           For macOS, use `--platform macOS`
        3. Drag the generated `Auth0.xcframework` from `Carthage/Build` into your Xcode project
        4. In your target's **General** settings, add `Auth0.xcframework` to **Frameworks, Libraries, and Embedded Content**
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure Auth0" stepNumber={3}>
    Create a new Auth0 application and configure callback URLs.

    1. Go to [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
    2. **Applications** > **Create Application** > Name it, select **Native** > **Create**
    3. In the **Settings** tab, note your **Client ID** and **Domain**
    4. Add the following URLs to **Allowed Callback URLs**:

    <Tabs>
      <Tab title="iOS">
        ```
        https://{yourDomain}/ios/YOUR_BUNDLE_IDENTIFIER/callback,
        YOUR_BUNDLE_IDENTIFIER://{yourDomain}/ios/YOUR_BUNDLE_IDENTIFIER/callback
        ```
      </Tab>

      <Tab title="macOS">
        ```
        https://{yourDomain}/macos/YOUR_BUNDLE_IDENTIFIER/callback,
        YOUR_BUNDLE_IDENTIFIER://{yourDomain}/macos/YOUR_BUNDLE_IDENTIFIER/callback
        ```
      </Tab>
    </Tabs>

    5. Add the following URLs to **Allowed Logout URLs**:

    <Tabs>
      <Tab title="iOS">
        ```
        https://{yourDomain}/ios/YOUR_BUNDLE_IDENTIFIER/callback,
        YOUR_BUNDLE_IDENTIFIER://{yourDomain}/ios/YOUR_BUNDLE_IDENTIFIER/callback
        ```
      </Tab>

      <Tab title="macOS">
        ```
        https://{yourDomain}/macos/YOUR_BUNDLE_IDENTIFIER/callback,
        YOUR_BUNDLE_IDENTIFIER://{yourDomain}/macos/YOUR_BUNDLE_IDENTIFIER/callback
        ```
      </Tab>
    </Tabs>

    6. Click **Save Changes**
  </Step>

  <Step title="Configure App Credentials" stepNumber={4}>
    Create `Auth0.plist` in your project directory:

    ```xml Auth0.plist theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>ClientId</key>
        <string>YOUR_AUTH0_CLIENT_ID</string>
        <key>Domain</key>
        <string>{yourDomain}</string>
    </dict>
    </plist>
    ```

    Drag `Auth0.plist` into Xcode and ensure "Add to target" is checked.
  </Step>

  <Step title="Create the Authentication Service" stepNumber={5}>
    Create `AuthenticationService.swift`:

    1. Right-click your project → **New File...** → **Swift File**
    2. Name it `AuthenticationService`
    3. Replace contents with:

    ```swift AuthenticationService.swift expandable lines theme={null}
    import Foundation
    import Auth0
    import Combine

    @MainActor
    class AuthenticationService: ObservableObject {
        @Published var isAuthenticated = false
        @Published var user: User?
        @Published var isLoading = false
        @Published var errorMessage: String?
        
        private let credentialsManager = CredentialsManager(authentication: Auth0.authentication())
        
        init() {
            Task {
                await checkAuthenticationStatus()
            }
        }
        
        private func checkAuthenticationStatus() async {
            isLoading = true
            defer { isLoading = false }
            
            guard let credentials = try? await credentialsManager.credentials() else {
                isAuthenticated = false
                return
            }
            
            isAuthenticated = true
            // Get user info from the ID token
            user = credentials.user
        }
        
        func login() async {
            isLoading = true
            errorMessage = nil
            defer { isLoading = false }
            
            do {
                let credentials = try await Auth0
                    .webAuth()
                    .scope("openid profile email offline_access")
                    .start()
                
                _ = credentialsManager.store(credentials: credentials)
                isAuthenticated = true
                // Get user info from the ID token
                user = credentials.user
            } catch {
                errorMessage = "Login failed: \(error.localizedDescription)"
            }
        }
        
        func logout() async {
            isLoading = true
            defer { isLoading = false }
            
            do {
                try await Auth0.webAuth().clearSession()
                _ = credentialsManager.clear()
                isAuthenticated = false
                user = nil
            } catch {
                errorMessage = "Logout failed: \(error.localizedDescription)"
            }
        }
    }
    ```
  </Step>

  <Step title="Create UI Components" stepNumber={6}>
    Create UI files and add code:

    ```bash theme={null}
    touch AuthenticatedView.swift UnauthenticatedView.swift ProfileCard.swift LoadingView.swift
    ```

    <AuthCodeGroup>
      ```swift ContentView.swift expandable lines theme={null}
      import SwiftUI

      struct ContentView: View {
          @StateObject private var authService = AuthenticationService()
          
          var body: some View {
              VStack(spacing: 30) {
                  Image(systemName: "shield.checkered")
                      .font(.system(size: 80))
                      .foregroundStyle(.blue)
                  
                  Text("Auth0 Sample")
                      .font(.largeTitle)
                      .fontWeight(.bold)
                  
                  if authService.isLoading {
                      LoadingView()
                  } else if authService.isAuthenticated {
                      AuthenticatedView(authService: authService)
                  } else {
                      UnauthenticatedView(authService: authService)
                  }
                  
                  Spacer()
              }
              .padding()
              .alert("Error", isPresented: .constant(authService.errorMessage != nil)) {
                  Button("OK") { authService.errorMessage = nil }
              } message: {
                  Text(authService.errorMessage ?? "")
              }
          }
      }
      ```

      ```swift AuthenticatedView.swift expandable lines theme={null}
      import SwiftUI

      struct AuthenticatedView: View {
          @ObservedObject var authService: AuthenticationService
          
          var body: some View {
              VStack(spacing: 24) {
                  HStack {
                      Image(systemName: "checkmark.circle.fill")
                          .foregroundColor(.green)
                      Text("Successfully authenticated!")
                          .font(.headline)
                          .foregroundColor(.green)
                  }
                  .padding()
                  .background(Color.green.opacity(0.1))
                  .cornerRadius(12)
                  
                  Text("Your Profile")
                      .font(.title2)
                      .fontWeight(.semibold)
                  
                  ProfileCard(user: authService.user)
                  
                  Button("Log Out") {
                      Task {
                          await authService.logout()
                      }
                  }
                  .buttonStyle(.borderedProminent)
                  .controlSize(.large)
                  .disabled(authService.isLoading)
              }
          }
      }
      ```

      ```swift UnauthenticatedView.swift expandable lines theme={null}
      import SwiftUI

      struct UnauthenticatedView: View {
          @ObservedObject var authService: AuthenticationService
          
          var body: some View {
              VStack(spacing: 24) {
                  Text("Get started by signing in to your account")
                      .font(.title3)
                      .multilineTextAlignment(.center)
                      .foregroundColor(.secondary)
                      .padding()
                      .background(Color(.secondarySystemBackground))
                      .cornerRadius(16)
                  
                  Button("Log In") {
                      Task {
                          await authService.login()
                      }
                  }
                  .buttonStyle(.borderedProminent)
                  .controlSize(.large)
                  .disabled(authService.isLoading)
              }
          }
      }
      ```

      ```swift ProfileCard.swift expandable lines theme={null}
      import SwiftUI
      import Auth0

      struct ProfileCard: View {
          let user: User?
          
          var body: some View {
              VStack(spacing: 16) {
                  if let user = user {
                      AsyncImage(url: URL(string: user.picture ?? "")) { image in
                          image.resizable().aspectRatio(contentMode: .fill)
                      } placeholder: {
                          Image(systemName: "person.circle.fill")
                              .font(.system(size: 80))
                              .foregroundColor(.gray)
                      }
                      .frame(width: 100, height: 100)
                      .clipShape(Circle())
                      
                      VStack(spacing: 8) {
                          if let name = user.name {
                              Text(name).font(.title2).fontWeight(.semibold)
                          }
                          if let email = user.email {
                              Text(email).foregroundColor(.secondary)
                          }
                      }
                  } else {
                      ProgressView()
                      Text("Loading profile...").foregroundColor(.secondary)
                  }
              }
              .padding()
              .background(Color(.tertiarySystemBackground))
              .cornerRadius(16)
          }
      }
      ```

      ```swift LoadingView.swift expandable lines theme={null}
      import SwiftUI

      struct LoadingView: View {
          var body: some View {
              VStack(spacing: 16) {
                  ProgressView().scaleEffect(1.2)
                  Text("Loading...").font(.headline).foregroundColor(.secondary)
              }
          }
      }
      ```
    </AuthCodeGroup>
  </Step>

  <Step title="Configure Authentication Flow (Optional)" stepNumber={7}>
    To improve the user experience, you can minimize system alerts in the following ways:

    1. Use Universal Links: This eliminates the 'Open in "AppName"?' prompt that appears during the redirect. Note: The ASWebAuthenticationSession permission alert will still appear.
    2. Use Ephemeral Sessions: This eliminates all permission alerts. Note: This disables Single Sign-On (SSO) and shared cookies.

    <Tip>
      **Skip this step** to use the default behavior with a permission alert. You can configure this later.
    </Tip>

    <Tabs>
      <Tab title="Universal Links">
        1. Auth0 Dashboard → **Applications** → Your app → **Settings** → **Advanced Settings** → **Device Settings**
        2. Add **Apple Team ID** and **bundle identifier** → **Save**
        3. Xcode: Target → **Signing & Capabilities** → **+ Capability** → **Associated Domains**
        4. Add: `webcredentials:{yourDomain}`

        <Warning>Requires: Paid Apple Developer account, iOS 17.4+/macOS 14.4+</Warning>

        <Tip>
          Best for production apps.
        </Tip>
      </Tab>

      <Tab title="Ephemeral Session">
        Add `.useEphemeralSession()` to the login call in `AuthenticationService.swift`:

        ```swift theme={null}
        // In the login() function
        let credentials = try await Auth0
            .webAuth()
            .scope("openid profile email offline_access")
            .useEphemeralSession()
            .start()
        ```

        <Info>
          When using ephemeral sessions, you don't need to call `clearSession()` on logout. Just clear the credentials from your app - there's no shared cookie to remove.
        </Info>

        <Tip>
          Quick setup, no alerts, but users must log in every time (no SSO).
        </Tip>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Run your app" stepNumber={8}>
    Press **⌘+R** in Xcode.

    1. Tap "Log In" → Permission alert (if using default) → Tap "Continue"
    2. Complete login in browser
    3. See your profile!
  </Step>
</Steps>

<Check>
  **Checkpoint**

  You now have a fully functional Auth0 login in your iOS or macOS app!
</Check>

***

## Troubleshooting & Advanced

<Accordion title="Common Issues & Solutions">
  ### Build errors: 'Auth0' module not found

  **Solutions**:

  1. **Swift Package Manager**: Check **Package Dependencies** → Verify `Auth0.swift` is listed
  2. **CocoaPods**: Ensure you're opening the `.xcworkspace` file, not `.xcodeproj`
  3. **Carthage**: Verify `Auth0.xcframework` is added to **Frameworks, Libraries, and Embedded Content**
  4. Clean and rebuild: **⌘+Shift+K** then **⌘+R**
  5. Restart Xcode if needed

  ### App crashes: 'Auth0.plist not found'

  **Fix**:

  1. Verify `Auth0.plist` is in your Xcode project navigator
  2. Select the file → Inspector → Ensure your app target is checked
  3. Confirm it has `ClientId` and `Domain` keys with your values
  4. **Alternative**: Use programmatic configuration (see Advanced Integration section below)

  ### Browser opens but never returns to app

  **Fix**:

  1. Check callback URLs in Auth0 Dashboard match your bundle identifier and platform exactly
  2. For iOS: URLs should contain `/ios/`, for macOS: `/macos/`
  3. Verify bundle identifier in Xcode matches Auth0 settings
  4. Ensure no typos in URLs (common: missing colons, wrong domain format)
  5. **Custom domain users**: Verify you're using your custom domain, not the Auth0 domain

  ### Permission alert appears every time

  This is standard iOS/macOS security behavior when using custom URL schemes. See **Step 6** to eliminate this alert using Universal Links or Ephemeral Sessions.
</Accordion>

<Accordion title="Custom Domain Configuration">
  If you're using a [custom domain](/customize/custom-domains), use its value instead of your Auth0 domain everywhere.

  **Example:** Use `login.example.com` instead of `tenant.auth0.com`

  This is **required** for certain features to work properly:

  * Update `Auth0.plist` with your custom domain
  * Use custom domain in callback/logout URLs
  * For Universal Links, use: `webcredentials:login.example.com`
</Accordion>

<Accordion title="Production Deployment">
  ### App Store Preparation

  * Configure Universal Links to eliminate the permission alert
  * Test on multiple platform versions and device sizes
  * Implement proper error handling for network failures
  * Add Privacy Usage descriptions if using Keychain with biometrics
  * Follow App Store Review Guidelines for authentication flows

  ### Security Best Practices

  * Never log sensitive authentication data in production
  * Implement App Transport Security (ATS) compliance
  * Use HTTPS for all network requests
  * **Do NOT** pin Auth0 API certificates - [Auth0 does not recommend this practice](/troubleshoot/product-lifecycle/past-migrations#avoid-pinning-or-fingerprinting-tls-certificates-for-auth0-endpoints)

  ### Performance Optimization

  * All async operations properly use `@MainActor` for UI updates
  * `@Published` properties use proper memory handling
  * Credentials are cached securely in the Keychain for offline access
  * User profile is retrieved from ID token (no additional network request)
</Accordion>

<Accordion title="Advanced Integration">
  ### Programmatic Configuration

  Instead of using `Auth0.plist`, you can pass credentials directly in your code. This is useful when credentials need to be dynamic or environment-specific (e.g., different Auth0 tenants for dev/staging/production).

  **Replace `Auth0.webAuth()` calls with `Auth0.webAuth(clientId:domain:)`:**

  ```swift theme={null}
  // In AuthenticationService.swift - login function
  func login() async {
      isLoading = true
      errorMessage = nil
      defer { isLoading = false }
      
      do {
          let credentials = try await Auth0
              .webAuth(clientId: "{yourClientId}", domain: "{yourDomain}")
              .scope("openid profile email offline_access")
              .start()
          
          _ = credentialsManager.store(credentials: credentials)
          isAuthenticated = true
          user = credentials.user
      } catch {
          errorMessage = "Login failed: \(error.localizedDescription)"
      }
  }

  // In AuthenticationService.swift - logout function
  func logout() async {
      isLoading = true
      defer { isLoading = false }
      
      do {
          try await Auth0.webAuth(clientId: "{yourClientId}", domain: "{yourDomain}").clearSession()
          _ = credentialsManager.clear()
          isAuthenticated = false
          user = nil
      } catch {
          errorMessage = "Logout failed: \(error.localizedDescription)"
      }
  }
  ```

  ### Enhanced Keychain Security with Biometrics

  Require Face ID or Touch ID to access stored credentials:

  ```swift theme={null}
  private let credentialsManager: CredentialsManager = {
      let manager = CredentialsManager(authentication: Auth0.authentication())
      manager.enableBiometrics(
          withTitle: "Unlock with Face ID", 
          cancelTitle: "Cancel", 
          fallbackTitle: "Use Passcode"
      )
      return manager
  }()
  ```

  When enabled, users must authenticate with biometrics before the SDK can retrieve stored credentials.

  ### Automatic Token Refresh

  The `CredentialsManager` automatically refreshes expired access tokens:

  ```swift theme={null}
  // Get credentials - automatically refreshes if expired
  func getAccessToken() async throws -> String {
      let credentials = try await credentialsManager.credentials()
      return credentials.accessToken
  }
  ```

  Use this pattern when making API calls that require an access token.

  ### Shared Credentials Across App Extensions

  For widgets, app extensions, or background tasks that need access tokens:

  ```swift theme={null}
  // Create a shared credentials manager with an app group
  let credentialsManager = CredentialsManager(
      authentication: Auth0.authentication(),
      storeKey: "credentials",
      storage: .shared(withIdentifier: "group.com.example.myapp")
  )
  ```

  **Requirements:**

  1. Enable **App Groups** capability in Xcode for all targets
  2. Use the same app group identifier across targets
  3. Configure the shared `CredentialsManager` in each target

  ### Authentication Flow Options Comparison

  | Feature                  | Universal Links              | Ephemeral Session | Default (Alert)  |
  | ------------------------ | ---------------------------- | ----------------- | ---------------- |
  | Permission Alert         | Reduced (no redirect prompt) | None              | Shows all alerts |
  | SSO Support              | Yes                          | No                | Yes              |
  | Apple Developer Account  | Required                     | Not required      | Not required     |
  | User Experience          | Best                         | Good              | Acceptable       |
  | Setup Complexity         | Medium                       | Easy              | Easy             |
  | Private Browsing Support | Yes                          | Yes               | No               |

  **Recommendations:**

  * **Production apps with SSO**: Universal Links (better UX, SSO support, requires Apple Developer account)
  * **Production apps without SSO**: Ephemeral Sessions (no alerts, simpler setup)
  * **Testing/Development**: Ephemeral Sessions (quick setup, cleanest UX)
  * **Quick start/Prototyping**: Default with alerts (no setup, can migrate later)
</Accordion>
