# jwt-kit **Repository Path**: spm_mirror1/jwt-kit ## Basic Information - **Project Name**: jwt-kit - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-04-14 - **Last Updated**: 2022-04-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README JWTKit Documentation Team Chat MIT License Continuous Integration Swift 5.2

🔑 JSON Web Token signing and verification (HMAC, RSA, ECDSA) using BoringSSL. ### Major Releases The table below shows a list of JWTKit major releases alongside their compatible Swift versions. |Version|Swift|SPM| |---|---|---| |4.0|5.2+|`from: "4.0.0"`| |3.0|4.0+|`from: "3.0.0"`| |2.0|3.1+|`from: "2.0.0"`| |1.0|3.1+|`from: "1.0.0"`| Use the SPM string to easily include the dependendency in your `Package.swift` file. ```swift .package(url: "https://github.com/vapor/jwt-kit.git", from: "4.0.0") ``` > Note: Prior to version 4.0, this package was included in `vapor/jwt.git`. ### Supported Platforms JWTKit supports the following platforms: - Ubuntu 16.04, 18.04, 20.04 - macOS 10.15, 11 - CentOS 8 - Amazon Linux 2 ## Overview JWTKit provides APIs for signing and verifying JSON Web Tokens ([RFC7519](https://tools.ietf.org/html/rfc7519)). It supports the following features: - Verifying (parsing) - Signing (serializing) - RSA (RS256, RS384, RS512) - ECDSA (ES256, ES384, ES512) - HMAC (HS256, HS384, HS512) - Claims (aud, exp, iss, etc) - JSON Web Keys (JWK, JWKS) This package ships a private copy of BoringSSL for cryptography. ## Vapor If you are using Vapor, check out the [JWT](https://github.com/vapor/jwt) package which makes it easier to configure and use JWTKit in your project. ## Getting Started To start verifying or signing JWT tokens, you will need an instance of `JWTSigners`. ```swift import JWTKit // Signs and verifies JWTs let signers = JWTSigners() ``` Let's add a simple HS256 signer for testing. HMAC signers can sign _and_ verify tokens. ```swift // Add HMAC with SHA-256 signer. signers.use(.hs256(key: "secret")) ``` For this example, we'll use the very secure key _secret_. ### Verifying Let's try to verify the following example JWT. ```swift let jwt = """ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsImV4cCI6NjQwOTIyMTEyMDAsImFkbWluIjp0cnVlfQ.lS5lpwfRNSZDvpGQk6x5JI1g40gkYCOWqbc3J_ghowo """ ``` You can inspect the contents of this token by visiting [jwt.io](https://jwt.io) and pasting the token in the debugger. Set the key in the "Verify Signature" section to `secret`. We need to create a struct conforming to `JWTPayload` that represents the JWT's structure. We'll use JWTKit's included [claims](#claims) to handle common fields like `sub` and `exp`. ```swift // JWT payload structure. struct TestPayload: JWTPayload, Equatable { // Maps the longer Swift property names to the // shortened keys used in the JWT payload. enum CodingKeys: String, CodingKey { case subject = "sub" case expiration = "exp" case isAdmin = "admin" } // The "sub" (subject) claim identifies the principal that is the // subject of the JWT. var subject: SubjectClaim // The "exp" (expiration time) claim identifies the expiration time on // or after which the JWT MUST NOT be accepted for processing. var expiration: ExpirationClaim // Custom data. // If true, the user is an admin. var isAdmin: Bool // Run any additional verification logic beyond // signature verification here. // Since we have an ExpirationClaim, we will // call its verify method. func verify(using signer: JWTSigner) throws { try self.expiration.verifyNotExpired() } } ``` Now that we have a `JWTPayload`, we can use `JWTSigners` to parse and verify the JWT. ```swift // Parses the JWT and verifies its signature. let payload = try signers.verify(jwt, as: TestPayload.self) print(payload) ``` If everything worked, you should see the payload printed: ```swift TestPayload( subject: "vapor", expiration: 4001-01-01 00:00:00 +0000, isAdmin: true ) ``` ### Signing We can also _generate_ JWTs, also known as signing. To demonstrate this, let's use the `TestPayload` from the previous section. ```swift // Create a new instance of our JWTPayload let payload = TestPayload( subject: "vapor", expiration: .init(value: .distantFuture), isAdmin: true ) ``` Then, pass the payload to `JWTSigners.sign`. ```swift // Sign the payload, returning a JWT. let jwt = try signers.sign(payload) print(jwt) ``` You should see a JWT printed. This can be fed back into the `verify` method to access the payload. ## JWK A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key ([RFC7517](https://tools.ietf.org/html/rfc7517)). These are commonly used to supply clients with keys for verifying JWTs. For example, Apple hosts their _Sign in with Apple_ JWKS at the following URL. ```http GET https://appleid.apple.com/auth/keys ``` You can add this JSON Web Key Set (JWKS) to your `JWTSigners`. ```swift import Foundation import JWTKit // Download the JWKS. // This could be done asynchronously if needed. let jwksData = try Data( contentsOf: URL(string: "https://appleid.apple.com/auth/keys")! ) // Decode the downloaded JSON. let jwks = try JSONDecoder().decode(JWKS.self, from: jwksData) // Create signers and add JWKS. let signers = JWTSigners() try signers.use(jwks: jwks) ``` You can now pass JWTs from Apple to the `verify` method. The key identifier (`kid`) in the JWT header will be used to automatically select the correct key for verification. > Note: As of writing, JWK only supports RSA keys. ## HMAC HMAC is the simplest JWT signing algorithm. It uses a single key that can both sign and verify tokens. The key can be any length. - `hs256`: HMAC with SHA-256 - `hs384`: HMAC with SHA-384 - `hs512`: HMAC with SHA-512 ```swift // Add HMAC with SHA-256 signer. signers.use(.hs256(key: "secret")) ``` ## RSA RSA is the most commonly used JWT signing algorithm. It supports distinct public and private keys. This means that a public key can be distributed for verifying JWTs are authentic while the private key that generates them is kept secret. To create an RSA signer, first initialize an `RSAKey`. This can be done by passing in the components. ```swift // Initialize an RSA key with components. let key = RSAKey( modulus: "...", exponent: "...", // Only included in private keys. privateExponent: "..." ) ``` You can also choose to load a PEM file: ```swift let rsaPublicKey = """ -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0cOtPjzABybjzm3fCg1aCYwnx PmjXpbCkecAWLj/CcDWEcuTZkYDiSG0zgglbbbhcV0vJQDWSv60tnlA3cjSYutAv 7FPo5Cq8FkvrdDzeacwRSxYuIq1LtYnd6I30qNaNthntjvbqyMmBulJ1mzLI+Xg/ aX4rbSL49Z3dAQn8vQIDAQAB -----END PUBLIC KEY----- """ // Initialize an RSA key with public pem. let key = RSAKey.public(pem: rsaPublicKey) ``` Use `.private` for loading private RSA pem keys. These start with: ``` -----BEGIN RSA PRIVATE KEY----- ``` Use `.certificate` for loading X.509 certificates. These start with: ``` -----BEGIN CERTIFICATE----- ``` Once you have the RSAKey, you can use it to create an RSA signer. - `rs256`: RSA with SHA-256 - `rs384`: RSA with SHA-384 - `rs512`: RSA with SHA-512 ```swift // Add RSA with SHA-256 signer. try signers.use(.rs256(key: .public(pem: rsaPublicKey))) ``` ## ECDSA ECDSA is a more modern algorithm that is similar to RSA. It is considered to be more secure for a given key length than RSA[1](#1). However, you should do your own research before deciding. Like RSA, you can load ECDSA keys using PEM files: ```swift let ecdsaPublicKey = """ -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2adMrdG7aUfZH57aeKFFM01dPnkx C18ScRb4Z6poMBgJtYlVtd9ly63URv57ZW0Ncs1LiZB7WATb3svu+1c7HQ== -----END PUBLIC KEY----- """ // Initialize an ECDSA key with public pem. let key = ECDSAKey.public(pem: ecdsaPublicKey) ``` Use `.private` for loading private ECDSA pem keys. These start with: ``` -----BEGIN PRIVATE KEY----- ``` Once you have the ECDSAKey, you can use it to create an ECDSA signer. - `es256`: ECDSA with SHA-256 - `es384`: ECDSA with SHA-384 - `es512`: ECDSA with SHA-512 ```swift // Add ECDSA with SHA-256 signer. try signers.use(.es256(key: .public(pem: ecdsaPublicKey))) ``` ## Claims JWTKit includes several helpers for implementing common [JWT claims](https://tools.ietf.org/html/rfc7519#section-4.1). |Claim|Type|Verify Method| |---|---|---| |`aud`|`AudienceClaim`|`verifyIntendedAudience(includes:)`| |`exp`|`ExpirationClaim`|`verifyNotExpired(currentDate:)`| |`jti`|`IDClaim`|n/a| |`iat`|`IssuedAtClaim`|n/a| |`iss`|`IssuerClaim`|n/a| |`locale`|`LocaleClaim`|n/a| |`nbf`|`NotBeforeClaim`|`verifyNotBefore(currentDate:)`| |`sub`|`SubjectClaim`|n/a| All claims should be verified in the `JWTPayload.verify` method. If the claim has a special verify method, you can use that. Otherwise, access the value of the claim using `value` and check that it is valid. --- This package was originally authored by the wonderful [@siemensikkema](http://github.com/siemensikkema). --- 1: [https://sectigostore.com/blog/ecdsa-vs-rsa-everything-you-need-to-know/](https://sectigostore.com/blog/ecdsa-vs-rsa-everything-you-need-to-know/)