Files
telecom-platform/sdk/swift/Sources/TelecomSDK/AuthProvider.swift
T
nutcas3 f6590462f5 docs: Add Swift SDK README with comprehensive API documentation and implement authentication provider with JWT support
- Add README.md with installation instructions, architecture overview, and API examples for all SDK modules
- Add AuthProvider class with API key and JWT token support (generateJWTToken, validateJWTToken, getHeaders, clearTokenCache methods)
- Add HTTPClient base class with retry logic, error handling, and timeout management
- Add GraphQLAPI, PaymentAPI, RatingPlanAPI, SubscriberAPI, SystemAPI, and UsageAPI modules
- Add TelecomSDK main client integrating all API
2026-04-25 04:53:22 +03:00

166 lines
5.2 KiB
Swift

import Foundation
import CryptoKit
/// Authentication provider for the Telecom SDK
public class AuthProvider {
private var apiKey: String?
private var jwtSecret: String?
private var tokenCache: String?
private var tokenExpiry: Date?
public init(apiKey: String? = nil, jwtSecret: String? = nil) {
self.apiKey = apiKey
self.jwtSecret = jwtSecret
}
/// Get authentication headers for API requests
public func getHeaders() -> [String: String] {
var headers: [String: String] = [
"Content-Type": "application/json",
"User-Agent": "Telecom-Swift-SDK/1.0.0"
]
if let apiKey = apiKey {
headers["X-API-Key"] = apiKey
}
if let token = tokenCache, isTokenValid() {
headers["Authorization"] = "Bearer \(token)"
}
return headers
}
/// Generate a JWT token for authentication
public func generateJWTToken(userID: String, expiryHours: Int, additionalClaims: [String: Any] = [:]) throws -> String {
guard let jwtSecret = jwtSecret else {
throw AuthError.jwtSecretNotConfigured
}
let now = Date()
let exp = now.addingTimeInterval(TimeInterval(expiryHours * 3600))
var claims: [String: Any] = [
"sub": userID,
"exp": Int(exp.timeIntervalSince1970),
"iat": Int(now.timeIntervalSince1970)
]
for (key, value) in additionalClaims {
claims[key] = value
}
let header: [String: Any] = [
"alg": "HS256",
"typ": "JWT"
]
let encodedHeader = base64URLEncode(header)
let encodedPayload = base64URLEncode(claims)
let signature = sign("\(encodedHeader).\(encodedPayload)", secret: jwtSecret)
let token = "\(encodedHeader).\(encodedPayload).\(signature)"
tokenCache = token
tokenExpiry = exp
return token
}
/// Validate a JWT token
public func validateJWTToken(_ token: String) throws -> [String: Any] {
guard let jwtSecret = jwtSecret else {
throw AuthError.jwtSecretNotConfigured
}
let parts = token.split(separator: ".", maxSplits: 2)
guard parts.count == 3 else {
throw AuthError.invalidTokenFormat
}
let encodedHeader = String(parts[0])
let encodedPayload = String(parts[1])
let signature = String(parts[2])
let expectedSignature = try sign("\(encodedHeader).\(encodedPayload)", secret: jwtSecret)
guard signature == expectedSignature else {
throw AuthError.invalidTokenSignature
}
guard let payloadData = base64URLDecode(encodedPayload) else {
throw AuthError.failedToDecodePayload
}
guard let payload = try? JSONSerialization.jsonObject(with: payloadData) as? [String: Any] else {
throw AuthError.failedToDecodePayload
}
if let exp = payload["exp"] as? Int {
if Date().timeIntervalSince1970 > Double(exp) {
throw AuthError.tokenExpired
}
}
return payload
}
/// Clear the cached JWT token
public func clearTokenCache() {
tokenCache = nil
tokenExpiry = nil
}
private func isTokenValid() -> Bool {
guard let tokenCache = tokenCache, let tokenExpiry = tokenExpiry else {
return false
}
return Date() < tokenExpiry
}
private func base64URLEncode(_ data: Any) -> String {
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
return ""
}
return jsonData.base64EncodedString()
.replacingOccurrences(of: "=", with: "")
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
private func base64URLDecode(_ data: String) -> Data? {
var base64String = data
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
while base64String.count % 4 != 0 {
base64String += "="
}
return Data(base64Encoded: base64String)
}
private func sign(_ data: String, secret: String) throws -> String {
guard let secretData = secret.data(using: .utf8),
let dataData = data.data(using: .utf8) else {
throw AuthError.signingFailed
}
let key = SymmetricKey(data: secretData)
let signature = HMAC<SHA256>.authenticationCode(for: dataData, using: key)
return Data(signature).base64EncodedString()
.replacingOccurrences(of: "=", with: "")
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
}
public enum AuthError: Error {
case jwtSecretNotConfigured
case invalidTokenFormat
case invalidTokenSignature
case failedToDecodePayload
case tokenExpired
case signingFailed
}