Swift lib for decoding, validating, signing and verifying JWT
Swift library for decoding, validating, signing and verifying JWT
HS256
HS384
HS512
RS256
RS384
RS384
iss
sub
aud
exp
nbf
iat
jti
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: "secret".dataUsingEncoding(NSUTF8StringEncoding)!, 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 = NSDate().dateByAddingTimeInterval(300)
payload.notBefore = NSDate()
payload.issuedAt = NSDate()
payload.jwtIdentifier = NSUUID().UUIDString
payload["customClaim"] = "customClaim"
//use HS256 to sign the token
let signer = HMACSignature(secret: "secret".dataUsingEncoding(NSUTF8StringEncoding)!, 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 ...) by using a keychain item import side effect.
let certificateData : NSData = //DER-encoded X.509 certificate
let publicKey : RSAKey = try RSAKey(certificateData : certificateData)
let p12Data : NSData //PKCS #12–formatted identity data
let identity : (publicKey : RSAKey, privateKey : RSAKey) = try RSAKey.keysFromPkcs12Identity(p12Data, passphrase : "pass")
let keyData : NSData
//import key into the keychain
let key : RSAKey = try RSAKey.registerOrUpdateKey(keyData, tag : "keyTag")
let modulusData : NSData
let exponentData : NSData
//import key into the keychain
let key : RSAKey = try RSAKey.registerOrUpdateKey(modulus : modulusData, exponent : exponentData, tag : "keyTag")
Retrieve or delete key from the keychain :
//get registered key
let key : RSAKey? = RSAKey.registeredKeyWithTag("keyTag")
//remove
RSAKey.removeKeyWithTag("keyTag")
A large part of the raw key import code is copied from the Heimdall library.
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 a String
RegisteredClaimValidator.subject
: sub
claim is defined and is a String
RegisteredClaimValidator.audience
: aud
claim is defined and is a String
or [String]
RegisteredClaimValidator.expiration
: exp
claim is defined, is an Integer
transformable to NSDate
, and is after current dateRegisteredClaimValidator.notBefore
: nbf
claim is defined, is an Integer
transformable to NSDate
, and is before current dateRegisteredClaimValidator.issuedAt
: iat
claim is defined, is an Integer
transformable to NSDate
RegisteredClaimValidator.jwtIdentifier
: jti
claim is defined and is a String
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 : NSData, signature : NSData) -> 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 : NSData) throws -> NSData
}