Learn Swift - Authentication & Authorization
Series/Learn Swift/Episode 12
Episode 12 of 23

Learn Swift - Authentication & Authorization

This episode covers authentication and authorization in Swift apps: implementing the OAuth 2.0 and OpenID Connect flow, integrating Sign in with Apple, managing access tokens and refresh tokens, plus consuming APIs securely with attention to HTTP security headers and session security.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Authentication proves who the user is; authorization determines what they're allowed to do. Episode 12 covers authentication and authorization in Swift apps: the OAuth 2.0 and OpenID Connect flow, integrating Sign in with Apple, and managing tokens correctly.

The most common mistake in the field isn't the login part, but token management afterward — tokens stored in the wrong place, expirations not handled, or refreshes done without protection. This episode builds the right habits from the start.

OAuth 2.0 and OpenID Connect

Understanding the Code Flow

OAuth 2.0 is an authorization framework, while OpenID Connect (OIDC) adds an authentication layer on top of it. The Authorization Code flow with PKCE is the standard pattern for mobile apps:

  1. The app opens a login page in the browser.
  2. The user logs in and approves the requested scopes.
  3. The authorization server returns an authorization code to the app.
  4. The app exchanges the code for an access token and a refresh token.
  5. The app uses the access token to call APIs.

The exchange can be tested directly with curl:

Exchange code for tokens
curl -X POST https://auth.example.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTH_CODE" \
  -d "client_id=CLIENT_ID" \
  -d "redirect_uri=myapp://callback"

curl -X POST ... -d "grant_type=authorization_code" exchanges an authorization code for tokens. On mobile, this flow runs through ASWebAuthenticationSession, which opens login in the system's most secure browser.

Calling APIs with an Access Token

The access token is sent via the Authorization header:

Request with bearer token
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
 
let (data, response) = try await URLSession.shared.data(for: request)

request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") authenticates the request. The Bearer format is the OAuth 2.0 standard. Access tokens are short-lived — typically 15 minutes to 1 hour — to limit the impact if one leaks.

Sign in with Apple

Trusted Identity in the Ecosystem

Sign in with Apple is a built-in identity provider that makes password-free login easy. Users only need Face ID or Touch ID, and their account is authenticated instantly. Its key differentiator: a one-tap login with Email Relay and an "Sign In with Apple" stop button.

Basic integration involves two parties:

  • Client: the login button uses ASAuthorizationAppleIDButton; the authentication result is an identity token and an authorization code.
  • Server: verifies the identity token (JWT) and exchanges the code for a refresh token.

The identity token received by the client is a JWT signed by Apple:

Inspect identity token payload
echo "$IDENTITY_TOKEN" | cut -d. -f2 | base64 -d

echo "$IDENTITY_TOKEN" | cut -d. -f2 | base64 -d decodes the JWT payload to see claims such as sub, email, and email_verified. Never trust a token without verifying the signature and issuer on the server side.

Token Management and Session Security

Storing and Refreshing Tokens

Access tokens are stored in the Keychain (episode 11), not UserDefaults. Refresh tokens — far more sensitive because they're long-lived — also go in the Keychain, ideally with restricted access:

Refreshing an access token
func tokenBaru() async throws -> String {
    var request = URLRequest(url: URL(string: "https://auth.example.com/token")!)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded",
                     forHTTPHeaderField: "Content-Type")
    let body = "grant_type=refresh_token&refresh_token=\(refreshToken)"
    request.httpBody = Data(body.utf8)
 
    let (data, _) = try await URLSession.shared.data(for: request)
    let hasil = try JSONDecoder().decode(TokenResponse.self, from: data)
    return hasil.accessToken
}

"grant_type=refresh_token&refresh_token=\(refreshToken)" exchanges the refresh token for a new access token. When an API request fails with status 401, the app refreshes once then retries the request — a pattern that keeps users logged in without interruption.

Session Security and HTTP Headers

Some key session security rules:

  • Access tokens are sent only over HTTPS.
  • Refresh tokens are used only at the registered refresh endpoint.
  • On logout, remove all tokens from the Keychain.
  • Revoke tokens periodically or when suspicious credential changes occur.

For web servers, also pay attention to security headers such as Strict-Transport-Security and X-Content-Type-Options — habits we'll deepen in episode 13.

Warning

Never store tokens in UserDefaults. A rooted or jailbroken device can extract them; the Keychain with proper access control adds an extra layer of protection.

Secure API Consumption

Response Validation and Compliance

Secure API consumption means validating every response — not just the HTTP status, but also the data structure and the values within. Use strict response types with Codable, and don't surface data you weren't expecting. Encode authorization claims into the model: only fetch data the user is actually entitled to.

Rate Limiting and Monitoring

On the app side, watch for a sudden spike in 401s as a signal of a leaked token or a revoked session. Combine this with structured logging (episode 21) so your team can detect abuse early.

Closing

Key takeaways:

  • OAuth 2.0 with Authorization Code and PKCE is the standard mobile authentication flow.
  • Access tokens are sent as Bearer in the Authorization header.
  • Sign in with Apple provides trusted identity with privacy support.
  • Access and refresh tokens go in the Keychain, never in UserDefaults.
  • On a 401, refresh the token once, then retry the request.
  • Validate every API response and monitor for authentication anomalies.

In the next episode, episode 13, we'll cover networking security and best practices — HTTPS and TLS, certificate pinning, secure network configuration on iOS and macOS, API request signing, secure headers, and observability for network failures with retry policies. Your networking becomes a fortress!

Learn Swift - Authentication & Authorization | Learn Swift