SwiftJWT

認証ライブラリJWTSwiftJSON Web TokeniOSmacOSLinuxトークン認証署名検証

認証ライブラリ

SwiftJWT

概要

SwiftJWTは、IBMが開発したSwift言語用のJSON Web Token(JWT)実装ライブラリです。iOS、macOS、Linux環境でJWTの作成、署名、検証を行うことができ、様々な署名アルゴリズムをサポートしています。軽量で高性能なセキュアなトークン認証システムを構築するのに適しています。

詳細

SwiftJWTは、RFC 7519に準拠したJWTの完全な実装を提供します。JWTの生成からトークンの検証まで、認証に必要な全ての機能を包含しており、特にサーバーサイドSwiftアプリケーションでの使用に最適化されています。

主要コンポーネント

  • JWT構造体: ヘッダーとクレームを含むJWTの表現
  • Claims: RFC 7519で定義された標準クレーム(ClaimsStandardJWT)
  • JWTSigner: 署名アルゴリズムを管理する構造体
  • JWTVerifier: トークンの検証機能

サポートされる署名アルゴリズム

  • RSA: RS256, RS384, RS512
  • ECDSA: ES256, ES384, ES512
  • HMAC: HS256, HS384, HS512
  • なし(none): 未署名トークン(開発用)

セキュリティ機能

  • デジタル署名による改ざん検知
  • 有効期限(exp)クレームによる時間制限
  • 発行者(iss)や対象者(aud)の検証
  • カスタムクレームのサポート

メリット・デメリット

メリット

  • 完全なSwift実装: ネイティブSwiftコードで高いパフォーマンス
  • クロスプラットフォーム: iOS、macOS、Linuxで動作
  • 標準準拠: RFC 7519完全対応
  • 多様なアルゴリズム: 複数の署名アルゴリズムサポート
  • 型安全: Swiftの型システムを活用した安全な実装
  • IBM サポート: Kituraチームによる保守

デメリット

  • JWE非対応: JWS(署名)のみサポート、暗号化(JWE)は未対応
  • 平文データ: クレームは Base64 エンコードのみで暗号化されない
  • Swift限定: Swift以外の言語では使用不可
  • 学習コスト: JWT の概念理解が必要

参考ページ

書き方の例

パッケージ導入

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

基本的なクレーム定義

import SwiftJWT

// カスタムクレーム構造体
struct MyClaims: Claims {
    let sub: String    // subject(ユーザーID)
    let exp: Date      // expiration(有効期限)
    let iat: Date      // issued at(発行時刻)
    let iss: String    // issuer(発行者)
    let name: String   // カスタムクレーム
    let admin: Bool    // カスタムクレーム
}

JWT生成と署名

import SwiftJWT
import Foundation

// HSアルゴリズム用のシークレット
let secret = "your-secret-key".data(using: .utf8)!
let signer = JWTSigner.hs256(key: secret)

// クレーム作成
let claims = MyClaims(
    sub: "user123",
    exp: Date(timeIntervalSinceNow: 3600), // 1時間後に期限切れ
    iat: Date(),
    iss: "myapp.com",
    name: "John Doe",
    admin: false
)

// JWT作成
let jwt = JWT(claims: claims)

// 署名付きトークン生成
do {
    let signedJWT = try jwt.sign(using: signer)
    print("Signed JWT: \(signedJWT)")
} catch {
    print("Error signing JWT: \(error)")
}

JWT検証

// JWT文字列をデコードして検証
let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

do {
    // JWTをデコード
    let jwt = try JWT<MyClaims>(jwtString: jwtString)
    
    // 署名を検証
    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)")
        
        // 有効期限チェック
        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署名アルゴリズム使用例

import SwiftJWT

// RSA秘密鍵(PKCS#8形式)
let privateKeyPEM = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
-----END PRIVATE KEY-----
"""

// RSA公開鍵
let publicKeyPEM = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu5...
-----END PUBLIC KEY-----
"""

// RSA256署名者と検証者を作成
let signer = JWTSigner.rs256(privateKey: privateKeyPEM.data(using: .utf8)!)
let verifier = JWTVerifier.rs256(publicKey: publicKeyPEM.data(using: .utf8)!)

// JWT作成と署名
let jwt = JWT(claims: claims)
let signedToken = try jwt.sign(using: signer)

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

標準クレーム使用例

// 標準的なJWTクレーム
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
)

エラーハンドリング

// 包括的なエラーハンドリング
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)")
    }
}