Introduction to OAuth 2.0 and JWT in Swift
Modern mobile and desktop applications in Swift (iOS/macOS) increasingly use the OAuth 2.0 protocol and the JWT (JSON Web Token) format for secure authorization. OAuth 2.0 provides delegated access to resources, while JWT allows compact and secure transmission of user data. In this article, we will break down how to implement a complete authorization chain in Swift: from obtaining a token to verifying and refreshing it.
We will cover:
- Basics of OAuth 2.0: flows and roles
- Structure of JWT and its decoding in Swift
- Practical implementation of authorization using
URLSession - Storing tokens in the Keychain and refreshing them
Basics of OAuth 2.0 for a Swift Developer
OAuth 2.0 is an authorization protocol that allows an application to obtain limited access to a user's resources without sharing their credentials. In the context of Swift applications, the Authorization Code Flow with PKCE (Proof Key for Code Exchange) is most commonly used for mobile apps.
Key Components of OAuth 2.0
- Resource Owner — the user, owner of the data
- Client — your Swift application
- Authorization Server — the server issuing tokens (e.g., Google, Facebook, your backend)
- Resource Server — the API providing protected data
A typical flow in Swift looks like this:
- The application opens a web view (SFSafariViewController or ASWebAuthenticationSession) for user authentication.
- After successful login, the server redirects to a callback URL with a temporary code.
- The application exchanges the code for an access token and a refresh token.
- The access token is used for API requests, and the refresh token is used to obtain a new access token when it expires.
Structure of JWT and Its Decoding in Swift
JWT consists of three parts separated by dots: header.payload.signature. Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikl2YW4gUGV0cm92IiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cDecoding the payload in Swift (without signature verification):
import Foundation
struct JWT { static func decodePayload(_ token: String) -> [String: Any]? { let segments = token.components(separatedBy: ".") guard segments.count == 3 else { return nil } var base64 = segments[1] .replacingOccurrences(of: "-", with: "+") .replacingOccurrences(of: "_", with: "/") let length = Double(base64.lengthOfBytes(using: .utf8)) let requiredLength = 4 * ceil(length / 4.0) let padding = Int(requiredLength - length) if padding > 0 { base64 += String(repeating: "=", count: padding) } guard let data = Data(base64Encoded: base64) else { return nil } return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] }}
// Usageif let payload = JWT.decodePayload("your.jwt.token") { print(payload["sub"] ?? "") print(payload["exp"] ?? "")}For full JWT signature verification on the client side, use the SwiftJWT or JWTDecode library, available via Swift Package Manager.
Implementing OAuth 2.0 and JWT in Swift
Step 1: Setting Up URLSession and Token Models
struct AuthToken: Codable { let accessToken: String let refreshToken: String? let expiresIn: Int let tokenType: String}
class AuthService { private let session = URLSession.shared