Przejdź do treści


JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Getting Started

The first step to using JWT is adding the dependency to your Package.swift.

// swift-tools-version:5.2
import PackageDescription

let package = Package(
    name: "my-app",
    dependencies: [
         // Other dependencies...
        .package(url: "", from: "5.0.0-beta"),
    targets: [
        .target(name: "App", dependencies: [
            // Other dependencies...
            .product(name: "JWT", package: "jwt")
        // Other targets...

If you edit the manifest directly inside Xcode, it will automatically pick up the changes and fetch the new dependency when the file is saved. Otherwise, run swift package resolve to fetch the new dependency.


The JWT module adds a new property jwt to Application that is used for configuration. To sign or verify JWTs, you will need to add a key. The simplest signing algorithm is HS256 or HMAC with SHA-256.

import JWT

// Add HMAC with SHA-256 signer.
await app.jwt.keys.addHMAC(key: "secret", digestAlgorithm: .sha256)


The await keyword is required because the key collection is an actor.

The HS256 signer requires a key to initialize. Unlike other signers, this single key is used for both signing and verifying tokens. Learn more about the available algorithms below.


Let's try to verify the following example JWT.


You can inspect the contents of this token by visiting 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 JWT's included claims to handle common fields like sub and exp.

// JWT payload structure.
struct TestPayload: JWTPayload {
    // 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 algorithm: some JWTAlgorithm) async throws {
        try self.expiration.verifyNotExpired()


Now that we have a JWTPayload, we can attach the JWT above to a request and use req.jwt to fetch and verify it. Add the following route to your project.

// Fetch and verify JWT from incoming request.
app.get("me") { req async throws -> HTTPStatus in
    let payload = try await req.jwt.verify(as: TestPayload.self)
    return .ok

The req.jwt.verify helper will check the Authorization header for a bearer token. If one exists, it will parse the JWT and verify its signature and claims. If any of these steps fail, a 401 Unauthorized error will be thrown.

Test the route by sending the following HTTP request.

GET /me HTTP/1.1
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsImV4cCI6NjQwOTIyMTEyMDAsImFkbWluIjp0cnVlfQ.lS5lpwfRNSZDvpGQk6x5JI1g40gkYCOWqbc3J_ghowo

If everything worked, a 200 OK response will be returned and the payload printed:

    subject: "vapor", 
    expiration: 4001-01-01 00:00:00 +0000, 
    isAdmin: true


This package can also generate JWTs, also known as signing. To demonstrate this, let's use the TestPayload from the previous section. Add the following route to your project.

// Generate and return a new JWT."login") { req async throws -> [String: String] in
    // Create a new instance of our JWTPayload
    let payload = TestPayload(
        subject: "vapor",
        expiration: .init(value: .distantFuture),
        isAdmin: true
    // Return the signed JWT
    return try await [
        "token": req.jwt.sign(payload, kid: "a"),

The req.jwt.sign helper will use the default configured signer to serialize and sign the JWTPayload. The encoded JWT is returned as a String.

Test the route by sending the following HTTP request.

POST /login HTTP/1.1

You should see the newly generated token returned in a 200 OK response.

   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsImV4cCI6NjQwOTIyMTEyMDAsImFkbWluIjp0cnVlfQ.lS5lpwfRNSZDvpGQk6x5JI1g40gkYCOWqbc3J_ghowo"


For more information on using JWT with Vapor's authentication API, visit Authentication → JWT.


Vapor's JWT API supports verifying and signing tokens using the following algorithms.


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
// Add HMAC with SHA-256 signer.
await app.jwt.keys.addHMAC(key: "secret", digestAlgorithm: .sha256)


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.


Vapor's JWT package does not support RSA keys with a size less than 2048 bits. In addition to this, since RSA is no longer recommended by NIST due to security reasons, RSA keys are gated behind an Insecure namespace to discourage their use.

To create an RSA signer, first initialize an RSAKey. This can be done by passing in the components.

// Initialize an RSA private key with components.
let key = try Insecure.RSA.PrivateKey(
    modulus: modulus, 
    exponent: publicExponent, 
    privateExponent: privateExponent

The initializer for the public key is similar.

// Initialize an RSA public key with components.
let key = try Insecure.RSA.PublicKey(
    modulus: modulus, 
    exponent: publicExponent

You can also choose to load a PEM file:

let rsaPublicKey = """
-----END PUBLIC KEY-----

// Initialize an RSA key with public pem.
let key = try Insecure.RSA.PublicKey(pem: rsaPublicKey)

Use Insecure.RSA.PrivateKey for loading private RSA PEM keys. These start with:


Once you have the RSA key, you can add it using the addRSA method.

// Add RSA with SHA-256 signer.
try await app.jwt.keys.addRSA(
    key: Insecure.RSA.PublicKey(pem: rsaPublicKey),
    digestAlgorithm: .sha256


In addition to standard RSA, Vapor's JWT package also supports RSA with PSS padding. This is considered more secure than standard RSA, however it is still discouraged in favor of other asymmetric algorithms like ECDSA. While PSS just uses a different padding scheme than standard RSA, the key generation and usage is the same as RSA.

let key = Insecure.RSA.PublicKey(pem: publicKey)
try app.jwt.keys.addPSS(key: key, digestAlgorithm: .sha256)


ECDSA is a more modern algorithm that is similar to RSA. It is considered to be more secure for a given key length than RSA1. However, you should do your own research before deciding.

Like RSA, you can load ECDSA keys using PEM files:

let ecdsaPublicKey = """
-----END PUBLIC KEY-----

// Initialize an ECDSA key with public PEM.
let key = try ES256PublicKey(pem: ecdsaPublicKey)

There are three ECDSA algorithms available, depending on the curve used: - ES256: ECDSA with a P-256 curve and SHA-256 - ES384: ECDSA with a P-384 curve and SHA-384 - ES512: ECDSA with a P-521 curve and SHA-512

All algorithms provide botha public key and a private key, such as ES256PublicKey and ES256PrivateKey.

You can also generate random ECDSA using the empty initializer. This is useful for testing.

let key = ES256PrivateKey()

Once you have the ECDSAKey, you can add it to the key collection using the addECDSA method.

// Add ECDSA with SHA-256 signer.
try await app.jwt.keys.addECDSA(key: ES256PublicKey(pem: ecdsaPublicKey))

Key Identifier (kid)

If you are using multiple algorithms, you can use key identifiers (kids) to differentiate them. When configuring an algorithm, pass the kid parameter.

// Add HMAC with SHA-256 signer named "a".
await app.jwt.keys.addHMAC(key: "foo", digestAlgorithm: .sha256, kid: "a")
// Add HMAC with SHA-256 signer named "b".
await app.jwt.keys.addHMAC(key: "bar", digestAlgorithm: .sha256, kid: "b")

When signing JWTs, pass the kid parameter for the desired signer.

// Sign using signer "a"
try await req.jwt.sign(payload, kid: "a")

This will automatically include the signer's name in the JWT header's "kid" field. When verifying the JWT, this field will be used to look up the appropriate signer.

// Verify using signer specified by "kid" header.
// If no "kid" header is present, default signer will be used.
let payload = try await req.jwt.verify(as: TestPayload.self)

Since JWKs already contain kid values, you do not need to specify them during configuration.

// JWKs already contain the "kid" field.
let jwk: JWK = ...
try await app.jwt.keys.use(jwk: jwk)


Vapor's JWT package includes several helpers for implementing common JWT claims.

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.


A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key (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.


You can add this JSON Web Key Set (JWKS) to your JWTSigners. You can then 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.

JWT issuers may rotate their JWKS meaning you need to re-download occasionally. See Vapor's supported JWT Vendors list below for APIs that do this automatically.


Vapor provides APIs for handling JWTs from the popular issuers below.


First, configure your Apple application identifier.

// Configure Apple app identifier. = "..."

Then, use the helper to fetch and verify an Apple JWT.

// Fetch and verify Apple JWT from Authorization header.
app.get("apple") { req async throws -> HTTPStatus in
    let token = try await
    print(token) // AppleIdentityToken
    return .ok


First, configure your Google application identifier and G Suite domain name.

// Configure Google app identifier and domain name. = "..." = "..."

Then, use the helper to fetch and verify a Google JWT.

// Fetch and verify Google JWT from Authorization header.
app.get("google") { req async throws -> HTTPStatus in
    let token = try await
    print(token) // GoogleIdentityToken
    return .ok


First, configure your Microsoft application identifier.

// Configure Microsoft app identifier. = "..."

Then, use the helper to fetch and verify a Microsoft JWT.

// Fetch and verify Microsoft JWT from Authorization header.
app.get("microsoft") { req async throws -> HTTPStatus in
    let token = try await
    print(token) // MicrosoftIdentityToken
    return .ok