> ## Documentation Index
> Fetch the complete documentation index at: https://docs-staging-quickstart-revamp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Login to Your iOS or macOS Application

<Tip>
  Don't have an Auth0 account? <a href="https://auth0.com/signup?utm_source=native-ios&utm_medium=docs&utm_campaign=get-started" target="_blank">Sign up for free and get started with Auth0's secure authentication and authorization platform</a>.
</Tip>

## Get Started

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

    **In Xcode:**

    1. **File** → **New** → **Project** (or **⌘+Shift+N**)
    2. Select **iOS** tab → **App** template
    3. Configure your project:
       * **Product Name**: `Auth0-iOS-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 iOS app with SwiftUI and Swift Package Manager support, perfect for Auth0 integration.
    </Tip>
  </Step>

  <Step title="Add Auth0 SDK via Swift Package Manager" stepNumber={2}>
    Add the Auth0 iOS SDK to your project using Swift Package Manager.

    **In Xcode:**

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

    **Package details:**

    * **Auth0.swift**: Main authentication SDK
    * **JWTDecode**: JWT token parsing utilities
    * **SimpleKeychain**: Secure iOS Keychain wrapper

    <Tip>
      Swift Package Manager automatically handles dependency resolution and updates. The Auth0 SDK will appear in your **Project Navigator** under **Package Dependencies**.
    </Tip>
  </Step>

  <Step title="Setup your Auth0 App" stepNumber={3}>
    Next up, you need to:

    1. Create a new Native Application on your Auth0 tenant
    2. Configure your iOS app with Auth0 credentials
    3. Add the Auth0 configuration file to your Xcode project

    ### Create Auth0 Application

    1. Go to the [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
    2. Click **Applications** → **Create Application**
    3. Name your application (e.g., "My iOS App")
    4. Choose **Native** as the application type
    5. Click **Create**

    ### Configure Application Settings

    In your Auth0 application settings:

    1. Go to the **Settings** tab
    2. Scroll down to **Application URIs**
    3. In **Allowed Callback URLs**, add both URLs below (replace placeholders):
       ```
       https://YOUR_AUTH0_DOMAIN/ios/YOUR_BUNDLE_IDENTIFIER/callback,
       YOUR_BUNDLE_IDENTIFIER://YOUR_AUTH0_DOMAIN/ios/YOUR_BUNDLE_IDENTIFIER/callback
       ```
    4. In **Allowed Logout URLs**, add the same URLs as above
    5. Click **Save Changes**

    **Example URLs:**

    ```
    https://dev-abc123.us.auth0.com/ios/com.example.MyApp/callback,
    com.example.MyApp://dev-abc123.us.auth0.com/ios/com.example.MyApp/callback
    ```

    ### Create Auth0.plist Configuration

    Create a configuration file in your project directory (same level as your `.xcodeproj` file) called `Auth0.plist`:

    ```xml Auth0.plist expandable lines
    <?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>YOUR_AUTH0_DOMAIN</string>
    </dict>
    </plist>
    ```

    Replace `YOUR_AUTH0_CLIENT_ID` and `YOUR_AUTH0_DOMAIN` with the actual values from your Auth0 application's **Settings** tab.

    ### Add Auth0.plist to Xcode Project

    1. **Drag and drop**: In Finder, drag the `Auth0.plist` file into your Xcode project navigator
    2. **Configure import**: In the dialog that appears:
       * ✅ Check "Add to target" for your app target
       * ✅ Check "Copy items if needed"
       * Click **Finish**
    3. **Verify**: The file should now appear in your Xcode project navigator

    <Warning>
      **Important**: The plist file must be added to your app target, not just the project. If authentication fails, verify the plist file is included in your target.
    </Warning>
  </Step>

  <Step title="Create the Authentication Service" stepNumber={4}>
    Create a centralized service to handle all authentication logic.

    **Add a new Swift file:**

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

    ```swift AuthenticationService.swift expandable lines
    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() {
            checkAuthenticationStatus()
        }
        
        private func checkAuthenticationStatus() {
            isLoading = true
            credentialsManager.credentials { result in
                DispatchQueue.main.async {
                    self.isLoading = false
                    switch result {
                    case .success(let credentials):
                        self.isAuthenticated = true
                        self.fetchUserInfo(with: credentials.accessToken)
                    case .failure:
                        self.isAuthenticated = false
                    }
                }
            }
        }
        
        func login() {
            isLoading = true
            errorMessage = nil
            
            Auth0.webAuth()
                .scope("openid profile email offline_access")
                .start { result in
                    DispatchQueue.main.async {
                        self.isLoading = false
                        switch result {
                        case .success(let credentials):
                            _ = self.credentialsManager.store(credentials: credentials)
                            self.isAuthenticated = true
                            self.fetchUserInfo(with: credentials.accessToken)
                        case .failure(let error):
                            self.errorMessage = "Login failed: \(error.localizedDescription)"
                        }
                    }
                }
        }
        
        func logout() {
            isLoading = true
            Auth0.webAuth()
                .clearSession { result in
                    DispatchQueue.main.async {
                        self.isLoading = false
                        switch result {
                        case .success:
                            _ = self.credentialsManager.clear()
                            self.isAuthenticated = false
                            self.user = nil
                        case .failure(let error):
                            self.errorMessage = "Logout failed: \(error.localizedDescription)"
                        }
                    }
                }
        }
        
        private func fetchUserInfo(with accessToken: String) {
            Auth0.authentication()
                .userInfo(withAccessToken: accessToken)
                .start { result in
                    DispatchQueue.main.async {
                        if case .success(let userInfo) = result {
                            self.user = userInfo
                        }
                    }
                }
        }
    }
    ```
  </Step>

  <Step title="Create Login, Logout and Profile Components" stepNumber={5}>
    Create the UI components that respond to authentication state.

    Create files

    ```shellscript
    touch AuthenticatedView.swift && touch UnauthenticatedView.swift && touch ProfileCard.swift && touch LoadingView.swift
    ```

    And add the following code snippets

    <CodeGroup>
      ```swift ContentView.swift expandable lines
      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 iOS 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
      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") {
                      authService.logout()
                  }
                  .buttonStyle(.borderedProminent)
                  .controlSize(.large)
                  .disabled(authService.isLoading)
              }
          }
      }
      ```

      ```swift UnauthenticatedView.swift expandable lines
      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") {
                      authService.login()
                  }
                  .buttonStyle(.borderedProminent)
                  .controlSize(.large)
                  .disabled(authService.isLoading)
              }
          }
      }
      ```

      ```swift ProfileCard.swift expandable lines
      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
      import SwiftUI

      struct LoadingView: View {
          var body: some View {
              VStack(spacing: 16) {
                  ProgressView().scaleEffect(1.2)
                  Text("Loading...").font(.headline).foregroundColor(.secondary)
              }
          }
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Configure Associated Domains (Optional)" stepNumber={6}>
    <Note>
      **Skip this step** if you're just getting started. This is for production apps with paid Apple Developer accounts.
    </Note>

    <Accordion title="Universal Links Setup (Production)">
      ### What are Universal Links?

      Universal Links use HTTPS URLs instead of custom URL schemes for enhanced security.

      ### Requirements

      * **Paid Apple Developer account**
      * **iOS 17.4+** or **macOS 14.4+**
      * **Xcode 15.3+**

      ### Configure Auth0 Dashboard

      1. Auth0 Dashboard → **Applications** → Your app → **Settings** → **Advanced Settings** → **Device Settings**
      2. Add your Apple Team ID and bundle identifier
      3. **Save Changes**

      ### Configure Xcode Project

      1. Select your app target → **Signing & Capabilities**
      2. **+ Capability** → **Associated Domains**
      3. Add: `webcredentials:YOUR_AUTH0_DOMAIN`

      Your app must be signed with your team certificate for this to work.
    </Accordion>
  </Step>

  <Step title="Run your app" stepNumber={7}>
    ```shellscript
    # In Xcode, press Cmd+R or click the Run button
    ```

    **Expected flow:**

    1. App launches with "Log In" button
    2. Tap "Log In" → iOS permission alert → Tap "Continue"
    3. Safari opens → Complete login → Returns to app
    4. Success! See your profile information

    <Warning>
      iOS will show a permission alert for your Auth0 domain. This is normal security behavior - tap **Continue**.
    </Warning>
  </Step>
</Steps>

<Check>
  **Checkpoint**

  You should now have a fully functional Auth0 login page running on your iOS simulator or device
</Check>

***

## Next up:

<Columns cols={2}>
  <Card title="Secure your API" icon="server" iconType="solid">
    Integrate the Auth0 SDK on your backend service to authenticate API calls
  </Card>

  <Card title="Build MFA" icon="user-check" iconType="solid">
    Go beyond basic authentication and learn how to enable MFA
  </Card>
</Columns>

***

## Troubleshooting & Advanced

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

  **Solutions**:

  1. Check **Package Dependencies** → Verify `Auth0.swift` is listed
  2. Clean and rebuild: **⌘+Shift+K** then **⌘+R**
  3. 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

  ### Safari opens but never returns to app

  **Fix**:

  1. Check callback URLs in Auth0 Dashboard match your bundle identifier exactly
  2. Verify bundle identifier in Xcode matches Auth0 settings
  3. Ensure no typos in URLs (common: missing colons, wrong domain format)

  ### Permission alert appears every time

  This is normal iOS security behavior. Consider Universal Links (Step 6) for production apps.
</Accordion>

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

  * Configure Associated Domains for Universal Links
  * Test on multiple iOS versions and device sizes
  * Implement proper error handling for network failures
  * Add Privacy Usage descriptions for Keychain access
  * Follow App Store Review Guidelines for authentication flows

  ### Security Considerations

  * Never log sensitive authentication data in production
  * Use Certificate Pinning for additional API security
  * Implement App Transport Security (ATS) compliance
  * Consider DeviceCheck for fraud prevention

  ### Performance Optimization

  * All Auth0 callbacks are dispatched to main queue appropriately
  * `@Published` properties use proper memory handling
  * App handles authentication state during background/foreground transitions
</Accordion>

<Accordion title="Advanced iOS Integration">
  ### Enhanced Keychain Security

  Implement biometric authentication for credential access:

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

  ### App Lifecycle Integration

  Handle authentication state during app lifecycle transitions:

  ```swift
  @main
  struct Auth0_iOS_SampleApp: App {
      @Environment(\.scenePhase) private var scenePhase
      @StateObject private var authService = AuthenticationService()
      
      var body: some Scene {
          WindowGroup {
              ContentView()
                  .environmentObject(authService)
                  .onChange(of: scenePhase) { newPhase in
                      if newPhase == .active {
                          authService.refreshTokenIfNeeded()
                      }
                  }
          }
      }
  }
  ```

  ### Token Refresh

  Automatically refresh expired tokens:

  ```swift
  func refreshTokenIfNeeded() {
      credentialsManager.credentials { result in
          DispatchQueue.main.async {
              switch result {
              case .success(let credentials):
                  self.credentials = credentials
              case .failure(let error):
                  if case .noCredentials = error {
                      self.isAuthenticated = false
                  }
              }
          }
      }
  }
  ```
</Accordion>
