Popular Searches
Popular Course Categories
Popular Courses

Managing API Keys, Tokens, and Authentication Credentials

Managing API Keys, Tokens, and Authentication Credentials

iOS App Security and Performance Optimization

Managing API Keys, Tokens, and Authentication Credentials in iOS

Course: iOS App Development

Topic: Managing API Keys, Tokens, and Authentication Credentials

Course Link: iOS App Development Training

Demo Link: Register for Course Demo

Website: JustAcademy


1. Introduction to API Keys, Tokens, and Authentication Credentials

Modern iOS applications communicate with remote servers to retrieve information, submit user requests, process payments, synchronize data, and provide personalized services. These communication processes often require authentication credentials to verify the identity of an application, user, or service.

API keys, access tokens, refresh tokens, passwords, and session credentials are commonly used in application authentication. Developers must manage these values carefully because exposing credentials can allow unauthorized individuals to access protected services or user information.

iOS provides security technologies such as Keychain Services, Secure Enclave, App Transport Security, and system authentication frameworks to help developers protect sensitive information.

Learning Objectives

  • Understand API keys, access tokens, refresh tokens, and authentication credentials.
  • Learn the difference between authentication and authorization.
  • Understand common authentication mechanisms used in iOS applications.
  • Store sensitive credentials securely using Keychain Services.
  • Manage token creation, expiration, renewal, and deletion.
  • Integrate authentication credentials with URLSession API requests.
  • Implement secure login and logout workflows.
  • Understand OAuth 2.0 and JSON Web Tokens.
  • Prevent credential exposure in source code, logs, and application storage.
  • Apply security best practices when communicating with backend services.

2. Understanding API Keys

An API key is a unique value used by an API provider to identify an application, project, or integration making requests to a service.

For example, an iOS weather application may communicate with a weather service using an API key. The service can use that key to identify the associated project, enforce quotas, and apply configured restrictions.

Example API Key

API_KEY = "example_public_key_123456"

The above value is only a demonstration. Real API keys must never be copied into publicly shared examples or repositories.

Common Uses of API Keys

  • Identifying an application when calling a third-party API.
  • Tracking API usage and request limits.
  • Applying service-level restrictions.
  • Associating requests with a registered project.
  • Controlling access to certain API resources.

Important Characteristics of API Keys

  • API keys are generally issued by an API provider or backend administrator.
  • They may identify an application or project rather than an individual user.
  • Some API keys are designed for public client-side use with restrictions.
  • Secret server-side API keys must never be embedded in an iOS application.
  • API keys should be restricted to the required services, APIs, and environments.

3. Understanding Authentication Tokens

An authentication token is a credential used to represent an authenticated user, session, or authorization grant. After a user signs in, the backend may issue a token that the application sends with later API requests.

Tokens reduce the need to send the user's password repeatedly. They can also contain or reference information about permissions, expiration, and the identity associated with a session.

Example Access Token

Authorization: Bearer example_access_token_123

The example token is fictional and must not be used for real authentication.

Common Types of Tokens

Token Type Description Purpose
Access Token Credential used to access protected API resources. Authorizes API requests.
Refresh Token Credential used to obtain a new access token. Maintains a session without repeatedly asking the user to sign in.
ID Token Token containing identity claims in OpenID Connect. Provides information about the authenticated user.
Session Token Credential representing an authenticated session. Allows the server to associate requests with a session.
One-Time Token Short-lived credential intended for a limited operation. Supports actions such as verification or password reset.

4. Difference Between API Keys, Passwords, and Tokens

Feature API Key Password Access Token
Purpose Identifies an application or project. Authenticates a user. Grants access to protected resources.
Issued By API provider or administrator. Created by the user or organization. Authentication or authorization server.
Typical Usage API identification and quota management. Login authentication. Authenticated API requests.
Expiration May be long-lived or rotated. Usually changed or reset by the user. Often short-lived.
Storage Depends on whether it is public or secret. Should not be stored as plain text. Secure storage such as Keychain.

5. Understanding Authentication and Authorization

Authentication and authorization are related security concepts, but they have different responsibilities.

Authentication

Authentication verifies who a user or application claims to be. For example, a user may authenticate by entering an email address and password or by using a supported identity provider.

Authorization

Authorization determines what an authenticated user or application is permitted to access. For example, a normal user may view their own profile, while an administrator may manage multiple user accounts.

Authentication Authorization
Verifies identity. Checks access permissions.
Answers: Who are you? Answers: What are you allowed to do?
Often happens during login. Applied when accessing protected resources.
May use passwords, biometrics, or identity providers. May use roles, scopes, policies, or permissions.

6. Understanding the iOS Keychain

Keychain Services is an Apple security technology used to store small pieces of sensitive information, such as passwords, cryptographic keys, certificates, and authentication tokens.

Unlike ordinary application preferences, Keychain is designed specifically for sensitive credential storage. It provides access controls and data protection features that help protect stored secrets.

Why Use Keychain?

  • Stores sensitive credentials more securely than ordinary preferences.
  • Supports access-control policies.
  • Provides configurable accessibility levels.
  • Can protect data using device security mechanisms.
  • Allows applications to retrieve credentials when required.
  • Supports secure storage of authentication tokens and passwords.

Keychain vs UserDefaults

Feature Keychain UserDefaults
Primary Purpose Secure credential storage. Application preferences and settings.
Suitable for Passwords Yes. No.
Suitable for Access Tokens Yes. No.
Suitable for Theme Preference Usually unnecessary. Yes.
Access Protection Supports Keychain access controls and data protection. Not designed as a secure secret store.
Example Refresh token. Dark mode preference.

7. Understanding Keychain Services

Keychain Services provides a set of APIs for adding, reading, updating, and deleting protected items.

Important Security framework functions include:

Function Purpose
SecItemAdd Adds a new item to the Keychain.
SecItemCopyMatching Searches for and retrieves a matching item.
SecItemUpdate Updates an existing item.
SecItemDelete Deletes a matching item.

8. Importing the Security Framework

To use Keychain Services in Swift, import Apple's Security framework.

import Security

This framework provides the constants, data types, and functions needed to manage Keychain items.

9. Creating a Keychain Helper

A Keychain helper centralizes credential storage operations. This avoids repeating low-level Security framework code throughout the application.

Example Keychain Helper

import Foundation
import Security

enum KeychainError: Error {
    case duplicateItem
    case itemNotFound
    case unexpectedStatus(OSStatus)
}

final class KeychainManager {

    static let shared = KeychainManager()

    private init() {}

    func save(
        _ value: String,
        forKey key: String
    ) throws {

        let data = Data(value.utf8)

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: data,
            kSecAttrAccessible as String:
                kSecAttrAccessibleWhenUnlockedThisDeviceOnly
        ]

        let status = SecItemAdd(
            query as CFDictionary,
            nil
        )

        guard status == errSecSuccess else {
            if status == errSecDuplicateItem {
                throw KeychainError.duplicateItem
            }

            throw KeychainError.unexpectedStatus(status)
        }
    }
}

This helper stores a string as a generic password item. The accessibility setting means the item is available when the device is unlocked and is not migrated to another device through backup restoration.

10. Retrieving Credentials from Keychain

After storing a token, the application can retrieve it when making authenticated API requests.

func read(forKey key: String) throws -> String {

    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key,
        kSecReturnData as String: true,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]

    var result: CFTypeRef?

    let status = SecItemCopyMatching(
        query as CFDictionary,
        &result
    )

    guard status != errSecItemNotFound else {
        throw KeychainError.itemNotFound
    }

    guard status == errSecSuccess,
          let data = result as? Data,
          let value = String(data: data, encoding: .utf8) else {
        throw KeychainError.unexpectedStatus(status)
    }

    return value
}

The application should handle missing items and unexpected Keychain errors without crashing. It should not print retrieved credentials to application logs.

11. Updating Credentials in Keychain

When a token is renewed or replaced, the application may need to update the existing Keychain item.

func update(
    _ value: String,
    forKey key: String
) throws {

    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key
    ]

    let attributes: [String: Any] = [
        kSecValueData as String: Data(value.utf8)
    ]

    let status = SecItemUpdate(
        query as CFDictionary,
        attributes as CFDictionary
    )

    guard status == errSecSuccess else {
        throw KeychainError.unexpectedStatus(status)
    }
}

Updating an existing item is different from adding a new one. If an item does not exist, the application should decide whether to create it or report an error.

12. Deleting Credentials from Keychain

Credentials should be deleted when the user logs out, a session is revoked, or the application no longer needs the stored value.

func delete(forKey key: String) throws {

    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key
    ]

    let status = SecItemDelete(
        query as CFDictionary
    )

    guard status == errSecSuccess ||
          status == errSecItemNotFound else {
        throw KeychainError.unexpectedStatus(status)
    }
}

Allowing deletion to succeed when an item is already absent makes logout and cleanup operations easier to repeat safely.

13. Improving Keychain Helper with Save-or-Update Logic

A practical credential manager should support both inserting a new item and replacing an existing item.

func saveOrUpdate(
    _ value: String,
    forKey key: String
) throws {

    let data = Data(value.utf8)

    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key
    ]

    let attributes: [String: Any] = [
        kSecValueData as String: data
    ]

    let updateStatus = SecItemUpdate(
        query as CFDictionary,
        attributes as CFDictionary
    )

    if updateStatus == errSecItemNotFound {
        var addQuery = query
        addQuery[kSecValueData as String] = data
        addQuery[kSecAttrAccessible as String] =
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly

        let addStatus = SecItemAdd(
            addQuery as CFDictionary,
            nil
        )

        guard addStatus == errSecSuccess else {
            throw KeychainError.unexpectedStatus(addStatus)
        }

    } else if updateStatus != errSecSuccess {
        throw KeychainError.unexpectedStatus(updateStatus)
    }
}

14. Understanding Access Token Expiration

Access tokens are often short-lived. A backend or identity provider may issue a token with an expiration time. After expiration, the token may no longer authorize protected API requests.

Applications should not assume that a token remains valid indefinitely. They should follow the authentication server's documented expiration and renewal process.

Example Token Response

{
    "access_token": "example_access_token",
    "refresh_token": "example_refresh_token",
    "token_type": "Bearer",
    "expires_in": 3600
}

The expires_in field commonly represents the access token's lifetime in seconds. The actual response format depends on the authentication provider.

15. Understanding Refresh Tokens

A refresh token is a credential used to obtain a new access token without asking the user to enter their password again. It is generally more sensitive than an access token because it can be used to continue or renew a session.

Typical Token Renewal Flow

  1. The user signs in successfully.
  2. The authentication server issues an access token and, where supported, a refresh token.
  3. The application stores the required credentials securely.
  4. The application uses the access token for protected API requests.
  5. The access token expires or becomes invalid.
  6. The application sends the refresh token to the authorized token endpoint.
  7. The server validates the refresh token.
  8. The server returns a new access token and may rotate the refresh token.
  9. The application securely updates its stored credentials.

Refresh tokens must be protected carefully. Follow the provider's rotation, reuse-detection, revocation, and expiration rules.

16. Implementing a Token Model in Swift

A token model can represent the authentication data returned by a backend.

struct AuthTokenResponse: Decodable {
    let accessToken: String
    let refreshToken: String?
    let tokenType: String
    let expiresIn: Int

    enum CodingKeys: String, CodingKey {
        case accessToken = "access_token"
        case refreshToken = "refresh_token"
        case tokenType = "token_type"
        case expiresIn = "expires_in"
    }
}

Using Decodable allows Swift to convert a JSON authentication response into a strongly typed model.

17. Sending Authenticated API Requests Using URLSession

After retrieving an access token from Keychain, the application can attach it to an API request using the HTTP Authorization header.

func fetchUserProfile(
    accessToken: String
) async throws -> Data {

    guard let url = URL(
        string: "https://api.example.com/profile"
    ) else {
        throw URLError(.badURL)
    }

    var request = URLRequest(url: url)

    request.httpMethod = "GET"
    request.setValue(
        "Bearer \(accessToken)",
        forHTTPHeaderField: "Authorization"
    )

    let (data, response) =
        try await URLSession.shared.data(for: request)

    guard let httpResponse =
        response as? HTTPURLResponse else {
        throw URLError(.badServerResponse)
    }

    guard (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }

    return data
}

Use HTTPS for authenticated API communication. The backend must validate the token and enforce authorization for the requested resource.

18. Handling Unauthorized API Responses

An API may return an HTTP 401 Unauthorized response when a token is missing, expired, invalid, or no longer accepted. The application should distinguish authentication failures from other server errors.

HTTP Status Meaning Typical Handling
200 Request succeeded. Process the response.
400 Invalid request. Review request parameters and payload.
401 Authentication is missing or invalid. Attempt the supported token renewal flow or request sign-in.
403 Access is forbidden. Show an appropriate access-denied message.
429 Too many requests. Respect rate limits and retry guidance.
500 Server error. Handle the service failure safely.

A 403 response does not necessarily mean that the user must sign in again. It may indicate that the authenticated user does not have permission to access the requested resource.

19. Implementing a Basic Authentication Manager

An authentication manager can coordinate login state, token storage, and logout operations.

import Foundation

final class AuthManager {

    static let shared = AuthManager()

    private let accessTokenKey = "access_token"
    private let refreshTokenKey = "refresh_token"

    private init() {}

    func saveTokens(
        accessToken: String,
        refreshToken: String?
    ) throws {

        try KeychainManager.shared.saveOrUpdate(
            accessToken,
            forKey: accessTokenKey
        )

        if let refreshToken = refreshToken {
            try KeychainManager.shared.saveOrUpdate(
                refreshToken,
                forKey: refreshTokenKey
            )
        }
    }

    func getAccessToken() throws -> String {
        return try KeychainManager.shared.read(
            forKey: accessTokenKey
        )
    }

    func logout() throws {
        try KeychainManager.shared.delete(
            forKey: accessTokenKey
        )

        try KeychainManager.shared.delete(
            forKey: refreshTokenKey
        )
    }
}

This example demonstrates the separation of authentication responsibilities from individual screens. A production implementation should also handle partial storage failures, server-side session revocation, and concurrent authentication operations.

20. Implementing a Login Workflow

A login workflow sends the user's credentials to an authentication service and receives an authentication response.

Typical Login Steps

  1. The user enters login information.
  2. The application validates required fields.
  3. The application sends credentials through a secure HTTPS request.
  4. The server verifies the credentials.
  5. The server returns an access token and optional refresh token.
  6. The application stores the tokens in Keychain.
  7. The application updates the authentication state.
  8. The user can access authorized application features.

Example Login Request Model

struct LoginRequest: Encodable {
    let email: String
    let password: String
}

Example Login Function

func login(
    email: String,
    password: String
) async throws {

    guard let url = URL(
        string: "https://api.example.com/login"
    ) else {
        throw URLError(.badURL)
    }

    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    request.setValue(
        "application/json",
        forHTTPHeaderField: "Content-Type"
    )

    request.httpBody = try JSONEncoder().encode(
        LoginRequest(
            email: email,
            password: password
        )
    )

    let (data, _) =
        try await URLSession.shared.data(for: request)

    let tokenResponse =
        try JSONDecoder().decode(
            AuthTokenResponse.self,
            from: data
        )

    try AuthManager.shared.saveTokens(
        accessToken: tokenResponse.accessToken,
        refreshToken: tokenResponse.refreshToken
    )
}

This is an illustrative example. A production login function must validate the HTTP response status, handle server error payloads, apply request timeouts, and avoid retaining the password longer than necessary.

21. Implementing Logout and Credential Cleanup

Logout should clear the application's local authentication state and, where supported, revoke the server-side session or refresh token.

Logout Workflow

  1. The user selects the logout option.
  2. The application sends a session-revocation request when supported.
  3. The server invalidates the relevant session or refresh token.
  4. The application deletes stored authentication credentials.
  5. The application clears user-specific cached information as appropriate.
  6. The application navigates to the login screen.
func performLogout() {
    do {
        try AuthManager.shared.logout()
        print("Local credentials cleared")
    } catch {
        print("Credential cleanup failed: \(error)")
    }
}

Do not rely only on deleting local tokens when the backend supports session revocation. Local deletion does not automatically invalidate a token already copied elsewhere.

22. Understanding OAuth 2.0

OAuth 2.0 is an authorization framework that allows an application to obtain limited access to protected resources without directly handling the user's account password for every service request.

For native iOS applications, the Authorization Code flow with Proof Key for Code Exchange (PKCE) is commonly recommended for public clients.

Basic OAuth 2.0 Flow

  1. The application starts an authorization request.
  2. The user is directed to the authorization server.
  3. The user authenticates and grants the requested permissions.
  4. The authorization server returns an authorization code.
  5. The application exchanges the code and PKCE verifier for tokens.
  6. The application stores the necessary tokens securely.
  7. The access token is used for authorized API requests.

Important OAuth 2.0 Terms

Term Description
Client The application requesting access.
Authorization Server The server responsible for authentication and authorization grants.
Resource Server The server hosting protected API resources.
Authorization Code A temporary code exchanged for tokens.
PKCE A mechanism that protects authorization-code exchanges for public clients.
Scope Defines the requested permissions.
Redirect URI The destination to which the authorization server returns the response.

For native applications, use an external user agent such as the system browser or an appropriate authentication session instead of embedding the login page in an ordinary web view. Follow the identity provider's documented redirect and PKCE requirements.

23. Understanding JSON Web Tokens (JWT)

A JSON Web Token is a compact token format that can carry claims between parties. It is commonly used in authentication and authorization systems.

A signed JWT typically contains three parts separated by periods:

header.payload.signature
Part Description
Header Contains token metadata, such as the signing algorithm and token type.
Payload Contains claims such as subject, issuer, audience, and expiration.
Signature Allows the recipient to verify token integrity and authenticity.

JWT payloads are generally encoded, not encrypted. Sensitive information should not be placed in a token unless an appropriate encryption design is used. The server must validate signatures and relevant claims such as issuer, audience, and expiration.

24. Managing API Keys in iOS Applications

API key management depends on the type of key and the security model of the API provider.

Public or Client-Side API Keys

Some services provide keys intended to be used by client applications. These keys should be restricted to the required APIs and protected with available platform or service restrictions. Developers should understand that a key embedded in an application binary can potentially be extracted.

Secret Server-Side API Keys

Secret keys must remain on a trusted backend. The iOS application should call the backend, and the backend should communicate with the third-party service using the secret key.

Recommended Architecture

iOS Application
      |
      | HTTPS Request
      v
Application Backend
      |
      | Server-side Secret API Key
      v
Third-Party API Provider

This design prevents a confidential service credential from being directly embedded in the distributed iOS application.

25. Secure Configuration and Environment Management

Applications often use different API endpoints and configuration values for development, testing, staging, and production.

Environment Purpose
Development Used by developers during implementation.
Testing Used for automated and manual testing.
Staging Used to verify the application in a production-like environment.
Production Used by the released application and real users.

Use build configurations and environment-specific settings to select the correct API base URL. Do not assume that putting a secret in an Xcode configuration file, property list, or compiled constant makes it confidential. Any value bundled into a client application may be extracted.

26. App Transport Security and Secure API Communication

App Transport Security (ATS) helps enforce secure network communication for applications using Apple's standard networking APIs.

HTTPS protects information while it travels between the application and the server. It helps prevent network attackers from reading or modifying data in transit.

Recommended Practices

  • Use HTTPS for all authentication and API requests.
  • Use valid TLS certificates.
  • Avoid sending credentials in URL query strings.
  • Keep ATS enabled whenever possible.
  • Avoid broad ATS exceptions.
  • Validate server responses and handle failures safely.
  • Never disable certificate validation to work around development problems.

27. Biometric Authentication and Keychain Access Control

Face ID and Touch ID can provide an additional user-verification step before allowing access to protected application information. LocalAuthentication supports biometric and device-passcode authentication.

Keychain access-control policies can also require user presence or biometric authentication before a protected item is accessed.

Example: Checking Biometric Availability

import LocalAuthentication

func authenticateUser(
    completion: @escaping (Bool) -> Void
) {
    let context = LAContext()
    var error: NSError?

    guard context.canEvaluatePolicy(
        .deviceOwnerAuthentication,
        error: &error
    ) else {
        completion(false)
        return
    }

    context.evaluatePolicy(
        .deviceOwnerAuthentication,
        localizedReason: "Access your secure account"
    ) { success, authenticationError in

        DispatchQueue.main.async {
            completion(success)
        }
    }
}

This example uses device-owner authentication, which can support available biometrics or the device passcode. Biometric authentication should not replace server-side authentication or authorization.

28. Common Credential Management Mistakes

Mistake Risk Recommended Solution
Hardcoding secret API keys Credentials can be extracted from the application. Keep confidential keys on the backend.
Storing tokens in UserDefaults Uses a storage mechanism not designed for secrets. Use Keychain Services.
Printing tokens in logs Credentials may appear in diagnostic output. Remove sensitive values from logs.
Using HTTP for login Credentials may be exposed in transit. Use HTTPS and secure transport settings.
Never expiring tokens Stolen credentials may remain usable for too long. Use suitable expiration and revocation policies.
Ignoring refresh-token rotation May allow reuse of compromised credentials. Follow server-side rotation and reuse-detection rules.
Keeping tokens after logout Another user may access the previous session. Clear local credentials and revoke sessions when supported.
Trusting JWT payload without validation Untrusted claims may be accepted. Validate tokens on the server.
Sharing credentials between users May expose one user's account to another. Associate credentials with the correct account and session.

29. Credential Rotation and Revocation

Credential rotation means replacing an existing key or token with a new one. Revocation means invalidating a credential so that it can no longer be used.

When Rotation Is Needed

  • A secret key may have been exposed.
  • A token is approaching expiration.
  • An employee or service account no longer needs access.
  • A security policy requires periodic replacement.
  • A refresh token has been rotated by the authentication provider.

Credential Revocation Workflow

  1. Identify the credential that needs to be revoked.
  2. Notify the trusted authentication or API provider.
  3. Revoke the credential using the provider's supported process.
  4. Remove the old value from local secure storage.
  5. Issue or obtain a replacement credential if required.
  6. Verify that the old credential can no longer access protected resources.

30. Practical Project: Secure Login and Token Management System

Project Objective: Build an iOS application that authenticates a user, securely stores access credentials, makes authenticated API requests, and clears credentials during logout.

Project Features

  • Login screen with email and password fields.
  • Secure login request using URLSession.
  • Token response decoding.
  • Keychain-based token storage.
  • Authenticated profile API request.
  • Unauthorized response handling.
  • Refresh-token workflow according to backend rules.
  • Logout and local credential cleanup.
  • Optional biometric protection for access to sensitive screens.

Step 1: Create a Token Storage Manager

Implement a reusable Keychain helper with save, read, update, and delete methods.

let accessTokenKey = "access_token"
let refreshTokenKey = "refresh_token"

try KeychainManager.shared.saveOrUpdate(
    "sample_access_token",
    forKey: accessTokenKey
)

Step 2: Create an Authentication Response Model

struct LoginResponse: Decodable {
    let accessToken: String
    let refreshToken: String?
    let expiresIn: Int

    enum CodingKeys: String, CodingKey {
        case accessToken = "access_token"
        case refreshToken = "refresh_token"
        case expiresIn = "expires_in"
    }
}

Step 3: Send the Login Request

func authenticate(
    email: String,
    password: String
) async throws -> LoginResponse {

    let url = URL(
        string: "https://api.example.com/login"
    )!

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue(
        "application/json",
        forHTTPHeaderField: "Content-Type"
    )

    request.httpBody = try JSONEncoder().encode(
        LoginRequest(
            email: email,
            password: password
        )
    )

    let (data, response) =
        try await URLSession.shared.data(for: request)

    guard let httpResponse =
        response as? HTTPURLResponse,
        (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.userAuthenticationRequired)
    }

    return try JSONDecoder().decode(
        LoginResponse.self,
        from: data
    )
}

Step 4: Save Authentication Tokens

func storeAuthentication(
    response: LoginResponse
) throws {

    try KeychainManager.shared.saveOrUpdate(
        response.accessToken,
        forKey: "access_token"
    )

    if let refreshToken = response.refreshToken {
        try KeychainManager.shared.saveOrUpdate(
            refreshToken,
            forKey: "refresh_token"
        )
    }
}

Step 5: Access a Protected API

func loadProfile() async throws -> Data {
    let token = try KeychainManager.shared.read(
        forKey: "access_token"
    )

    return try await fetchUserProfile(
        accessToken: token
    )
}

Step 6: Implement Logout

func logout() {
    do {
        try AuthManager.shared.logout()
        print("User logged out successfully")
    } catch {
        print("Unable to clear credentials")
    }
}

Expected Result

The user can sign in, receive authentication tokens, store them securely in Keychain, and use the access token to retrieve protected information. When the user logs out, local credentials are removed and the backend session is revoked when the service supports revocation.

31. Testing and Debugging Credential Management

Credential management must be tested carefully because authentication failures can affect the entire application.

Testing Checklist

  1. Test successful login with valid credentials.
  2. Test login with incorrect credentials.
  3. Verify that tokens are stored in Keychain.
  4. Verify that tokens can be retrieved correctly.
  5. Test updating an existing Keychain item.
  6. Test behavior when a Keychain item does not exist.
  7. Test an expired access token.
  8. Test refresh-token renewal and rotation.
  9. Test a revoked refresh token.
  10. Test API requests without an access token.
  11. Test logout and credential deletion.
  12. Verify that credentials do not appear in logs or crash reports.
  13. Test application behavior after device restart.
  14. Test behavior when biometric authentication is unavailable.

32. Best Practices for Managing API Keys and Credentials

  • Never embed confidential server-side API keys in the iOS application.
  • Use a trusted backend for operations requiring secret credentials.
  • Store access and refresh tokens in Keychain.
  • Choose Keychain accessibility settings according to the application's requirements.
  • Use HTTPS for all authentication requests.
  • Never store passwords in plain text.
  • Do not print tokens, passwords, or API keys in logs.
  • Use short-lived access tokens when supported by the authentication design.
  • Protect refresh tokens and follow rotation policies.
  • Clear credentials during logout.
  • Revoke server-side sessions when supported.
  • Validate authorization on the backend for every protected resource.
  • Use OAuth 2.0 Authorization Code with PKCE for suitable native-app authentication flows.
  • Do not treat a decoded JWT payload as proof of authenticity.
  • Use biometric authentication as an additional local protection layer when appropriate.
  • Restrict public API keys to the required services and application environments.
  • Keep development, staging, and production configurations separate.
  • Review credential exposure risks before releasing the application.

33. Practice Exercises

  1. Create a Swift Keychain helper to store a sample access token.
  2. Implement a function to retrieve a stored token.
  3. Add update and delete methods to the Keychain helper.
  4. Compare Keychain and UserDefaults through a small demonstration project.
  5. Create a login API request using URLSession.
  6. Decode an authentication response using Codable.
  7. Attach a Bearer token to a protected API request.
  8. Handle HTTP 401 and 403 responses separately.
  9. Implement a refresh-token workflow using a test backend.
  10. Build a logout function that clears local credentials.
  11. Explore OAuth 2.0 Authorization Code with PKCE.
  12. Implement biometric authentication before opening a protected screen.

34. Interview Questions and Answers

Q1. What is an API key?

Answer: An API key is a credential used by an API provider to identify an application or project and apply access restrictions or usage limits.

Q2. What is an access token?

Answer: An access token is a credential used to access protected resources on a server.

Q3. What is a refresh token?

Answer: A refresh token is used to obtain a new access token without requiring the user to sign in again, subject to the authentication provider's rules.

Q4. What is Keychain in iOS?

Answer: Keychain is Apple's secure credential storage technology for information such as passwords, authentication tokens, certificates, and cryptographic keys.

Q5. Why should tokens not be stored in UserDefaults?

Answer: UserDefaults is designed for application preferences, not sensitive credential storage. Keychain provides security features specifically intended for protected items.

Q6. What is the difference between authentication and authorization?

Answer: Authentication verifies identity, while authorization determines what resources or operations that identity is allowed to access.

Q7. What is the purpose of SecItemAdd?

Answer: SecItemAdd inserts a new item into the Keychain.

Q8. What is the purpose of SecItemCopyMatching?

Answer: SecItemCopyMatching searches for and retrieves a matching Keychain item.

Q9. What is OAuth 2.0?

Answer: OAuth 2.0 is an authorization framework that allows applications to obtain limited access to protected resources.

Q10. What is PKCE?

Answer: PKCE is an extension to the OAuth authorization-code flow that helps protect the code exchange, especially for public clients such as native mobile applications.

Q11. What is a JWT?

Answer: JSON Web Token is a compact token format used to carry claims between parties. A signed JWT commonly contains a header, payload, and signature.

Q12. Why should secret API keys not be embedded in an iOS app?

Answer: Application binaries can be inspected and reverse-engineered. Confidential server-side credentials should remain on a trusted backend.

Q13. What happens when an access token expires?

Answer: The server may reject protected API requests. The application can use the supported refresh-token flow or require the user to authenticate again.

Q14. What is token revocation?

Answer: Token revocation invalidates a credential so that it can no longer be used to access protected resources.

Q15. How can iOS applications improve credential security?

Answer: They can use Keychain, HTTPS, short-lived access tokens, secure refresh-token handling, backend authorization, credential rotation, and careful logging practices.

35. Quick Revision Checklist

  • Understand API keys, passwords, access tokens, and refresh tokens.
  • Differentiate authentication from authorization.
  • Understand the role of Keychain Services.
  • Know SecItemAdd, SecItemCopyMatching, SecItemUpdate, and SecItemDelete.
  • Store sensitive credentials securely.
  • Retrieve tokens before making protected API requests.
  • Use the Authorization Bearer header correctly.
  • Understand access-token expiration and refresh-token renewal.
  • Handle HTTP 401 and 403 responses correctly.
  • Understand OAuth 2.0 and PKCE.
  • Know the structure and security properties of JWTs.
  • Keep confidential API keys on the backend.
  • Use HTTPS and secure network communication.
  • Clear credentials during logout.
  • Test credential storage, renewal, revocation, and failure scenarios.

36. Conclusion

Managing API keys, tokens, and authentication credentials is an essential part of building secure iOS applications. API keys identify applications or projects, access tokens authorize protected API requests, and refresh tokens can support session renewal.

Keychain Services provides a secure mechanism for storing sensitive credentials, while URLSession supports authenticated communication with backend services. OAuth 2.0, PKCE, token expiration, and credential revocation help establish a more reliable authentication architecture.

Developers should avoid embedding confidential secrets in application binaries, use HTTPS, validate authorization on the server, protect tokens, and remove credentials when they are no longer needed. These practices help protect user accounts and reduce the risk of unauthorized access.

37. Additional Learning Resources

Previous Next
whatsapp