SwiftJWT

authentication-libraryJWTSwiftJSON-Web-TokeniOSmacOSLinuxtoken-authenticationsigningverification

Authentication Library

SwiftJWT

Overview

SwiftJWT is a JSON Web Token (JWT) implementation library for Swift developed by IBM. It enables creation, signing, and verification of JWTs on iOS, macOS, and Linux environments, supporting various signing algorithms. It's suitable for building lightweight, high-performance, and secure token authentication systems.

Details

SwiftJWT provides a complete implementation of JWTs compliant with RFC 7519. It encompasses all authentication-related functionalities from JWT generation to token verification, and is particularly optimized for use in server-side Swift applications.

Key Components

  • JWT Structure: Representation of JWT containing headers and claims
  • Claims: Standard claims defined in RFC 7519 (ClaimsStandardJWT)
  • JWTSigner: Structure that manages signing algorithms
  • JWTVerifier: Token verification functionality

Supported Signing Algorithms

  • RSA: RS256, RS384, RS512
  • ECDSA: ES256, ES384, ES512
  • HMAC: HS256, HS384, HS512
  • None: Unsigned tokens (for development)

Security Features

  • Tamper detection through digital signatures
  • Time limitation via expiration (exp) claims
  • Verification of issuer (iss) and audience (aud)
  • Custom claims support

Pros and Cons

Pros

  • Pure Swift Implementation: High performance with native Swift code
  • Cross-Platform: Works on iOS, macOS, and Linux
  • Standards Compliant: Full RFC 7519 compliance
  • Multiple Algorithms: Support for various signing algorithms
  • Type Safety: Safe implementation leveraging Swift's type system
  • IBM Support: Maintained by the Kitura team

Cons

  • No JWE Support: Only supports JWS (signing), no encryption (JWE)
  • Plain Text Data: Claims are only Base64 encoded, not encrypted
  • Swift Only: Cannot be used with languages other than Swift
  • Learning Curve: Requires understanding of JWT concepts

Reference Links

Code Examples

Package Installation

// Package.swift
dependencies: [
    .package(url: "https://github.com/Kitura/Swift-JWT.git", from: "3.6.200")
],
targets: [
    .target(
        name: "YourTarget",
        dependencies: ["SwiftJWT"]
    )
]

Basic Claims Definition

import SwiftJWT

// Custom claims structure
struct MyClaims: Claims {
    let sub: String    // subject (user ID)
    let exp: Date      // expiration
    let iat: Date      // issued at
    let iss: String    // issuer
    let name: String   // custom claim
    let admin: Bool    // custom claim
}

JWT Generation and Signing

import SwiftJWT
import Foundation

// Secret for HS algorithms
let secret = "your-secret-key".data(using: .utf8)!
let signer = JWTSigner.hs256(key: secret)

// Create claims
let claims = MyClaims(
    sub: "user123",
    exp: Date(timeIntervalSinceNow: 3600), // Expires in 1 hour
    iat: Date(),
    iss: "myapp.com",
    name: "John Doe",
    admin: false
)

// Create JWT
let jwt = JWT(claims: claims)

// Generate signed token
do {
    let signedJWT = try jwt.sign(using: signer)
    print("Signed JWT: \(signedJWT)")
} catch {
    print("Error signing JWT: \(error)")
}

JWT Verification

// Decode and verify JWT string
let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

do {
    // Decode JWT
    let jwt = try JWT<MyClaims>(jwtString: jwtString)
    
    // Verify signature
    let verifier = JWTVerifier.hs256(key: secret)
    let isValid = jwt.verify(using: verifier)
    
    if isValid {
        print("JWT is valid")
        print("User: \(jwt.claims.name)")
        print("Admin: \(jwt.claims.admin)")
        
        // Check expiration
        if jwt.claims.exp > Date() {
            print("Token is not expired")
        } else {
            print("Token has expired")
        }
    } else {
        print("JWT signature is invalid")
    }
} catch {
    print("Error verifying JWT: \(error)")
}

RSA Signing Algorithm Example

import SwiftJWT

// RSA private key (PKCS#8 format)
let privateKeyPEM = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
-----END PRIVATE KEY-----
"""

// RSA public key
let publicKeyPEM = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu5...
-----END PUBLIC KEY-----
"""

// Create RSA256 signer and verifier
let signer = JWTSigner.rs256(privateKey: privateKeyPEM.data(using: .utf8)!)
let verifier = JWTVerifier.rs256(publicKey: publicKeyPEM.data(using: .utf8)!)

// Create and sign JWT
let jwt = JWT(claims: claims)
let signedToken = try jwt.sign(using: signer)

// Verify
let decodedJWT = try JWT<MyClaims>(jwtString: signedToken)
let isValid = decodedJWT.verify(using: verifier)

Standard Claims Example

// Standard JWT claims
struct StandardClaims: Claims {
    let iss: String?    // issuer
    let sub: String?    // subject
    let aud: [String]?  // audience
    let exp: Date?      // expiration
    let nbf: Date?      // not before
    let iat: Date?      // issued at
    let jti: String?    // JWT ID
}

let standardClaims = StandardClaims(
    iss: "myapp.com",
    sub: "user123",
    aud: ["api.myapp.com"],
    exp: Date(timeIntervalSinceNow: 3600),
    nbf: Date(),
    iat: Date(),
    jti: UUID().uuidString
)

Error Handling

// Comprehensive error handling
func processJWT(_ tokenString: String) {
    do {
        let jwt = try JWT<MyClaims>(jwtString: tokenString)
        let verifier = JWTVerifier.hs256(key: secret)
        
        guard jwt.verify(using: verifier) else {
            print("Invalid signature")
            return
        }
        
        guard jwt.claims.exp > Date() else {
            print("Token expired")
            return
        }
        
        print("Valid token for user: \(jwt.claims.name)")
        
    } catch JWTError.invalidJWTString {
        print("Invalid JWT format")
    } catch JWTError.failedVerification {
        print("Signature verification failed")
    } catch {
        print("Unexpected error: \(error)")
    }
}