Skip to content

cfinis-verimatrix/JSONWebToken

 
 

Repository files navigation

JSONWebToken

Swift library for decoding, validating, signing and verifying JWT

Features

  • Verify and sign :
    • HMAC HS256 HS384 HS512
    • RSASSA-PKCS1-v1_5 RS256 RS384 RS384
  • Validate (optionally) all registered claims :
    • Issuer iss
    • Subject sub
    • Audience aud
    • Expiration Time exp
    • Not Before nbf
    • Issued At iat
    • JWT ID jti
  • No external dependencies : CommonCrypto and Security framework are used for signing and verifying
  • Extensible : add your own claim validator and sign operations

Carthage compatible

Usage

Decode & Validation

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

Sign

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

RSASSA-PKCS1-v1_5 Signature

Keys

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.

Verify

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)
...

Sign

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
...

Validation

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
}

Claim validation

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 date
  • RegisteredClaimValidator.notBefore : nbf claim is defined, is an Integer transformable to NSDate, and is before current date
  • RegisteredClaimValidator.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" }

Unsupported signature

Verify

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
}

Sign

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
}

Test

Test samples are generated using pyjwt Python library

About

Swift lib for decoding, validating, signing and verifying JWT for iOS and tvOS

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Swift 92.5%
  • Python 5.3%
  • Other 2.2%