Swift library for decoding, validating, signing and verifying JWT
- Verify and sign :
- HMAC
HS256
HS384
HS512
- RSASSA-PKCS1-v1_5
RS256
RS384
RS384
- HMAC
- Validate (optionally) all registered claims :
- Issuer
iss
- Subject
sub
- Audience
aud
- Expiration Time
exp
- Not Before
nbf
- Issued At
iat
- JWT ID
jti
- Issuer
- No external dependencies : CommonCrypto and Security framework are used for signing and verifying
- Extensible : add your own claim validator and sign operations
import JSONWebToken
let rawJWT: String
let jwt: JSONWebToken = try JSONWebToken(string: rawJWT)
//create the validator by combining other validators with the & or | operator
let validator = RegisteredClaimValidator.expiration &
RegisteredClaimValidator.notBefore.optional &
HMACSignature(secret: Data("secret".utf8), hashFunction: .sha256)
/*
- not expired
- can be used now (optional : a jwt without nbf will be valid)
- signed with HS256 and the key "secret"
*/
let validationResult = validator.validateToken(jwt)
guard case ValidationResult.Success = validationResult else { return }
//use the token and access the payload
let issuer: String? = jwt.payload.issuer
let customClaim = jwt.payload["customClaim"] as? String
import JSONWebToken
//build the payload
var payload = JSONWebToken.Payload()
payload.issuer = "http://kreactive.com"
payload.subject = "antoine"
payload.audience = ["com.kreactive.app"]
payload.expiration = Date().dateByAddingTimeInterval(300)
payload.notBefore = Date()
payload.issuedAt = Date()
payload.jwtIdentifier = UUID().uuidString
payload["customClaim"] = "customClaim"
//use HS256 to sign the token
let signer = HMACSignature(secret: Data("secret".utf8), hashFunction: .sha256)
//build the token, signer is optional
let jwt = try JSONWebToken(payload: payload, signer: signer)
let rawJWT: String = jwt.rawString
Keys are represented by the RSAKey
struct, wrapping a SecKeyRef
.
The preferred way of importing public keys is to use a DER-encoded X.509
certificate (.cer), and for private keys a PKCS#12
(.p12) identity.
It's also possible to import raw representation of keys (X509, public pem, modulus/exponent ...).
let certificateData: Data = //DER-encoded X.509 certificate
let publicKey: RSAKey = try RSAKey(certificateData: certificateData)
let p12Data: Data //PKCS #12–formatted identity data
let identity: (publicKey: RSAKey, privateKey: RSAKey) = try RSAKey.keysFromPkcs12Identity(p12Data, passphrase: "pass")
let keyData: Data
let key: RSAKey = try RSAKey(keyData: keyData)
let modulusData: Data
let exponentData: Data
let key: RSAKey = try RSAKey(modulus: modulusData, exponent: exponentData)
Use RSAPKCS1Verifier
as validator to verify token signature :
let jwt: JSONWebToken
let publicKey: RSAKey
let validator = RegisteredClaimValidator.expiration &
RegisteredClaimValidator.notBefore.optional &
RSAPKCS1Verifier(key: publicKey, hashFunction: .sha256)
let validationResult = validator.validateToken(jwt)
...
Use RSAPKCS1Signer
to generate signed token:
let payload: JSONWebToken.Payload
let privateKey: RSAKey
let signer = RSAPKCS1Signer(hashFunction: .sha256, key: privateKey)
let jwt = try JSONWebToken(payload: payload, signer: signer)
let rawJWT: String = jwt.rawString
...
Validators (signature and claims) implement the protocol JSONWebTokenValidatorType
public protocol JSONWebTokenValidatorType {
func validateToken(token: JSONWebToken) -> ValidationResult
}
Implementing this protocol on any class
or struct
allows it to be combined with other validator using the |
or &
operator.
The validation method returns a ValidationResult
:
public enum ValidationResult {
case Success
case Failure(ErrorType)
public var isValid: Bool
}
You can implement a claim validator with the ClaimValidator
struct :
public struct ClaimValidator<T>: JSONWebTokenValidatorType {
}
let validator: ClaimValidator<Int> = ClaimValidator(key: "customClaim", transform: { (jsonValue: AnyObject) throws -> Int in
guard let numberValue = jsonValue as? NSNumber else {
throw ClaimValidatorError(message: "customClaim value \(jsonValue) is not the expected Number type")
}
return numberValue.integerValue
}).withValidator { 1..<4 ~= $0 }
All registered claims validators are implemented :
RegisteredClaimValidator.issuer
:iss
claim is defined and is aString
RegisteredClaimValidator.subject
:sub
claim is defined and is aString
RegisteredClaimValidator.audience
:aud
claim is defined and is aString
or[String]
RegisteredClaimValidator.expiration
:exp
claim is defined, is anInteger
transformable toDate
, and is after current dateRegisteredClaimValidator.notBefore
:nbf
claim is defined, is anInteger
transformable toDate
, and is before current dateRegisteredClaimValidator.issuedAt
:iat
claim is defined, is anInteger
transformable toDate
RegisteredClaimValidator.jwtIdentifier
:jti
claim is defined and is aString
And it can be extended :
let myIssuerValidator = RegisteredClaimValidator.issuer.withValidator { $0 == "kreactive" }
Implement the SignatureValidator
protocol on any class
or struct
to add and unsupported signature algorithm validator.
public protocol SignatureValidator: JSONWebTokenValidatorType {
func canVerifyWithSignatureAlgorithm(alg: SignatureAlgorithm) -> Bool
func verify(input: Data, signature: Data) -> Bool
}
Implement the TokenSigner
protocol on any class
or struct
to sign token with unsupported signature algorithm.
public protocol TokenSigner {
var signatureAlgorithm: SignatureAlgorithm {get}
func sign(input: Data) throws -> Data
}