Support for JWT (JSON Web Tokens) in Vapor.
Original author
- Siemen Sikkema, @siemensikkema
Vapor JWT provider
License: MIT License
Support for JWT (JSON Web Tokens) in Vapor.
Original author
Hi, what would be the prefered way to retrieve the userId from the token? I don't want to validate a userId claim against a known Id but extract the id from the token.
I'm using Firebase to manage my users, and I use this library to validate JWT
tokens sent by Firebase. In their documentation, they state that I must validate that the Issued-at time
is in the past. I don't think I can do this with the current implementation, as IssuedAtClaim
forces comparison to a specific time.
if open Playground, No such module 'JWT'
if import swift files to a new project, No such module 'JSON'
Can tell me how to fix these errors?
Thanks!!!
I couldn't find documentation on JWT 4.
It would be very helpful if someone provide some documentation or some example codes about Vapor 4 + JWT 4.
Since we are still using cocoapods in our older project it would be great to have a pods to install your library
The patch in #102 introduced a subtle bug: In case where the r
or s
value of the signature is a "small" number, BN_bn2bin
will actually produce less than 32
bytes per value. In this case some 0
padding is necessary to make sure that the final signature is 2 x 32 = 64 bytes
long. See eg: kylebrowning/APNSwift#48
IssuedAtClaim
should be a TimeBasedClaim
rather than an EqualityClaim
.
The verify
method should check that the given value is not in the future.
The link on the README file to read the docs
is not have the version 3.0
Hello,
Did you plan to support Xcode projects using Cocoapods or Carthage?
If necessary, I can contribute.
Thanks!
Hi. I want to implement this Library on Swift 3 .. Is it compatible with Swift 3? Do you have Pod for the same ?
As of the current state of the beta branch, only the HMAC algorithms have been implemented. There should also be implementations for the RSA algorithms.
Developers who want to create protected routes using JWTs reasonably look towards Auth and to this package (JWT). Currently, though, the two cannot work with one another. Most of the pieces are in place, but they lack an easy-to-use integration layer.
Auth is the starting point by providing TokenAuthenticatable
and TokenAuthenticationMiddleware
which both use an associated TokenType
which must conform to Token
.
This package provides a concrete JWT
type but since it doesn't conform to Token
it is ineligible to use currently as a TokenType
.
I'm not entirely sure what the missing pieces are or should be, but @anthonycastelli shared with me an implementation, that while it didn't leverage the pieces from Auth, does outline a good starting point by creating a JWTMiddleware
and JWTService
(as well as an app-specific JWTPayload
to be used in a JWT
).
When i add the new vapor/jwt package:
.Package(url:"https://github.com/vapor/jwt.git", majorVersion: 0)
to my Package.swift, vapor xcode
gives error:
swift-package: error: The dependency graph could not be satisfied (https://github.com/vapor/crypto.git)
Thoughts?
Playground execution is failing with the following message:
Playground execution failed: error: missing required module 'CTLS'
I get the same error in an unrelated project but with CSQLite
, another C based module mapped to swift. It seems these kind of modules don't work well with playgrounds anymore. Does anyone know a solution?
remote: Linking ./.build/release/App
remote: /tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/Droplet+Setup.swift.o:/tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/Misc/String+E164Format.swift.o:function _::_TTSf4g_n_n___TFC3App14AuthMiddleware7respondfzT2toC4HTTP7Request10chainingToPS1_9Responder(Response) static const: error: undefined reference to '_TFE8VaporJWTPS_16ClaimsVerifiable12verifyClaimsfGSaPS_5Claim(bool) static'
remote: /tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/Droplet+Setup.swift.o:/tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/Misc/String+E164Format.swift.o:function _::_TTSf4g_n_n___TFC3App14AuthMiddleware7respondfzT2toC4HTTP7Request10chainingToPS1_9Responder(Response) static const: error: undefined reference to '_TFE8VaporJWTPS_16ClaimsVerifiable12verifyClaimsfGSaPS_5Claim(bool) static'
remote: /tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/RenewalTokenMiddleware.swift.o:/tmp/build_9e6662da1ad3a4a4089c8520505ca138/.build/release/App.build/Misc/String+E164Format.swift.o:function _::_TTSf4g_n_n___TFC3App22RenewalTokenMiddleware7respondfzT2toC4HTTP7Request10chainingToPS1_9Responder(Response) static const: error: undefined reference to '_TFE8VaporJWTPS_16ClaimsVerifiable12verifyClaimsfGSaPS_5Claim(bool) static'
remote: clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)
remote: :0: error: link command failed with exit code 1 (use -v to see invocation)
remote: :0: error: build had 1 command failures
it runs locally on mac with no issue, any ideas?
Hi,
How to install using Pod?
Thanks,
Pravin
Hi, I can't build a newly downloaded version of master on my mac :
.../jwt-master-2/Packages/CTLS-0.1.1/shim.h:4:10: 'openssl/conf.h' file not found
.../jwt-master-2/Packages/Crypto-2.0.0-alpha.4/Sources/Crypto/Cipher/Cipher+Method.swift:1:8: Could not build Objective-C module 'CTLS'
NB : I did got it working couple weeks ago !
App crashes in Base64URLTranscoder.base64URLEncode() for release builds only, works fine in Xcode. Crash log attached.
OS X version: 10.12.4
Xcode version: 8.3
Swift version: Apple Swift version 3.1 (swiftlang-802.0.48 clang-802.0.38)
Packages.swift:
.Package(url: "https://github.com/qutheory/vapor.git", majorVersion: 1),
.Package(url: "https://github.com/vapor/jwt.git", majorVersion: 1),
Packages.pins:
{
"package": "Vapor",
"reason": null,
"repositoryURL": "https://github.com/qutheory/vapor.git",
"version": "1.5.13"
},
{
"package": "JWT",
"reason": null,
"repositoryURL": "https://github.com/vapor/jwt.git",
"version": "1.0.0"
},
This may or may not be a general issue, but raising it for visibility.
When authenticating with Firebase, you use a JWT generated on Vapor, passed to the mobile app, and then passed to Firebase. Documentation of the process and JWT specification can be found here.
JWT Payload:
{
"iss": "[email protected]",
"aud": [
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
],
"iat": 1499885888,
"exp": 1499888888,
"sub": "[email protected]",
"uid": "xxxxxxxx"
}
When authenticating with Firebase from my iOS app with this JWT payload, I was receiving this error:
ERROR: Optional(Error Domain=FIRAuthErrorDomain Code=17000 "The custom token format is incorrect. Please check the documentation." UserInfo={NSLocalizedDescription=The custom token format is incorrect. Please check the documentation., error_name=ERROR_INVALID_CUSTOM_TOKEN})
The incorrect format was due to the aud
field being an array, not a single string.
I solved this by creating a custom claim:
struct SingleAudienceClaim: EqualityClaim {
public static var name = "aud"
public let value: String
init(string: String) {
self.value = string
}
public init?(_ node: Node) {
guard let string = node.string else {
return nil
}
self.init(string: string)
}
public var node: Node {
return .string(value)
}
}
I'm new to using JWT (and server-side in general), so not certain if this simply is a very specific issue with Firebase, or may reoccur across other sources.
Another solution could be the make the naming of AudienceClaim
more explicit that it is for multiple audience values?
HI man, i just newbie from swift and web service, would you tell me why i am always got true on token validate, this is my code
let jwt = try JWT(payload: Node(ExpirationTimeClaim(Date() + 1)),signer: HS256(key: "secret"))
return try JSON(node: [ "error_code" : "0", "token" : jwt.createToken(), "valid" : "60 second"])
and then i call isValidate like you did :
let jwt3 = try JWT(token: token)
let isValid = try jwt3.verifySignatureWith(HS256(key: "secret"))
if (isValid==false){
return try JSON(node: [ "error_code" : "1000", "message" : "Token is expired" ])
}
thanks man
ECDSA algorithms haven't been implemented yet. ES256 algorithm is needed to generate JWT for Apple service such as MapKit JS and MusicKit.
Would it be useful to have StringBacked as a public Protocol, so a custom claim can be StringBacked?
I'm getting a linking error on linux complaining about the new x509 method.
let certificate = try self.fetchSigningKey(for: keyId).extractRSACertificate()
let signer = try RS256(x509Cert: certificate)
Linking ./.build/release/App
/home/xxx/.build/release/App.build/Collections/AuthenticationCollection.swift.o:/home/xxx/.build/release/App.build/Stripe/Models/Invoice/Invoice.swift.o:function TFFC3App24AuthenticationCollection5buildFP7Routing12RouteBuilder_T_U0_FzC4HTTP7RequestPS3_21ResponseRepresentable: error: undefined reference to '_TFE3JWTPS_9RSASignerCfzT8x509CertGSaVs5UInt8(long long)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
:0: error: link command failed with exit code 1 (use -v to see invocation)
:0: error: build had 1 command failures
error: exit(1): /home/xxx/.swiftenv/versions/3.1/usr/bin/swift-build-tool -f /home/xxx/.build/release.yaml
Building Project [Failed]
Error: execute(1)
Vapor 2.0.3
JWT 2.1.0
When decoding a JWT from a string, I get this error :
▿ Jay.JayError.extraTokensFound
▿ extraTokensFound: Jay.ByteReader #0
▿ content: 28 elements
- 123
- 34
- 116
- 121
- 112
- 34
- 58
- 34
- 74
- 87
- 84
- 34
- 44
- 34
- 97
- 108
- 103
- 34
- 58
- 34
- 72
- 83
- 50
- 53
- 54
- 34
- 125
- 0
- cursor: 27
The JWT was eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.(...).(...)
.
This only happens when a Base64URL-encoded segment has n
characters, where n % 4 = 0
.
After investigating the issue, I've noticed that BytesTranscoder
adds a 4 characters-long padding in such cases when converting to Base64 (for example, "abcd"
becomes "abcd===="
and eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
becomes eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9====
).
This extra padding then interpreted as a null byte by Data(base64Encoded:)
. But according to Wikipedia, when n % 4 = 0
no padding characters should be added.
Hi,
Do you have any plans for introducing specific license to this project?
I'd like to use a part of this library in my open source project if possible, though lack of license makes it a bit unclear if I can do so, especially that I don't really want to depend on the whole package, only on the ecliptic curve algorithm implementation.
This is causing some symbol reference failures when building on Linux in release mode
https://github.com/vapor/jwt/blob/master/Sources/JWT/Claims/TimeBasedClaim.swift#L13
error: undefined reference to '_TIFE3JWTPS_14TimeBasedClaimcFT10createDateFT_V10Foundation4Date6leewaySi_xA_'
Maybe a cursory look at the code wasn't enough, but where do I plug in my private EC key for signing the message?
Hi all,
Is it possible to sign a RS256 JWT token with a string of the private keys similar to
"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtN7LQq7l9a9....SUug==\n-----END RSA PRIVATE KEY-----\n"
Many thanks in advance
I just added this to my Package.swift and did a vapor clean; vapor fetch and got the following.
Charlies-Air:GotYourBackServer charlie$ vapor clean
Cleaning [Done]
Charlies-Air:GotYourBackServer charlie$ vapor fetch
No Packages folder, fetch may take a while...
Fetching Dependencies [Failed]
Error: /usr/bin/git clone --recursive --depth 10 https://github.com/vapor/Crypto.git /Users/charlie/GotYourBackServer/Packages/Crypto.git
fatal: destination path '/Users/charlie/GotYourBackServer/Packages/Crypto.git' already exists and is not an empty directory.
swift-package: error: Failed to clone https://github.com/vapor/Crypto.git to /Users/charlie/GotYourBackServer/Packages/Crypto.git
Any thoughts on how to resolve this?
Hi Siemen, very nice library.
Do you have by any chance a working example with a ES256 signing?
I tried reproducing your steps below with secp521r1 in the keys generation and built the JWT with the private one and verified the signature with the public key. Unfortunately I get a (VaporJWT.JWTError error 1.)
This is my implementation:
guard let privateKey = drop.config["keys","jwtPrivateKey"]?.string else{
throw Abort.custom(status: .badRequest, message: "The private key is not set")
}
guard let publicKey = drop.config["keys","jwtPublicKey"]?.string else{
throw Abort.custom(status: .badRequest, message: "The public key is not set")
}
do {
let jwt = try JWT(payload: Node(ExpirationTimeClaim(Date() + 3600)),
signer: ES256(key: publicKey))
let token = try jwt.createToken()
print(token)
} catch{
print(error.localizedDescription)
}
do {
let jwt = try JWT(token: bearer.string)
let isValid = try jwt.verifySignatureWith(ES256(key: privateKey))
print(isValid)
print(bearer)
} catch {
print(error.localizedDescription)
}`
Any insight on that? Thanks a lot.
PS: The example works correctly if I use a HS256 signature.
Unless I'm mistaken, the RSA signers should have a separate public and private key. The private key is used for signing and the public key is used for verifying the signature.
Right now you can only add one key that is used for both.
Hi ,
can you please provide version of lib that support swift 2.1 ? It would be of G8 help !
There seems to be a problem with the latest version 3.0.0 and Swift 4.1
I get this error when compiling on Mac:
.../.build/checkouts/JWTVapor.git--6738815433567181283/Sources/JWTVapor/JWTService.swift:20:36: error: '&' used with non-inout argument of type 'JWT<_>'
let data = try signer.sign(&jwt)
I think jwt is not an inout, can the &
be removed?
Looking at old commits (pre v4) there was added support for certificates. Is this in version 4?
I'm trying to use these google certs:
https://www.googleapis.com/robot/v1/metadata/x509/[email protected]
With this code: (ignore the hard code for testing!)
let publicKey = certs["f5c9aebe234da6016bd7b949168b8cd5b4ec9eeb"]!
let publicSigner = try JWTSigner.rs256(key: .public(pem: publicKey.bytes))
I get the following error when creating a public signer.
Fatal error: Error raised at top level: JWTKit error: signing algorithm error: bioConversionFailure: file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ErrorType.swift, line 200 2020-05-16 20:07:25.947242-0700 Run[9217:3171678] Fatal error: Error raised at top level: JWTKit error: signing algorithm error: bioConversionFailure: file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ErrorType.swift, line 200
Thanks!
Apparently the JWT 3 doesn't support JWK signing yet. This would be a good feature to implement, especially for those of us that used it in Vapor 2 🙂.
Wanted to play with this, please fix issues to support carthage.
RSA signing can fail if the key is not large enough (for instance 256 or 512). The return code of the RSA_sign
method should be checked and a more helpful error should be thrown.
Hello, I found that JWTs signed with JWTKit ES256
are not correctly validated by jwt.io. The reason is, that the signature is stored in the DER
format instead of the format specified in the JWS spec.
For a possible fix, please take a look at the discussion at kylebrowning/APNSwift#36 and kylebrowning/APNSwift#37, kylebrowning/APNSwift#38.
[JWT.JWTError: JWT error: Could not create public key]
I am attempting to validate the signature of a web token I have received from a third party service (Microsoft) using the following.
let signer = try RS256(x509Cert: cert[0].bytes)
try jwt.verifySignature(using: signer)
Where cert[0]
is a string representation of the certificate that is retrieved from a jwk(JSON object).
I am unable to create the public key using this method and without much documentation to go on I haven't been able to determine if I am doing this incorrectly or not. Any help would be much appreciated.
Seems that JWTs should always be base 64 url encoded as per the RFC:
JWTs encode claims to be transmitted as a JSON
object (as defined in RFC 4627 [RFC4627]) that is base64url encoded
and digitally signed or HMACed and/or encrypted.
Perhaps we should remove the encoding
types to reduce the possibility of errors (as can happen when trying to parse tokens from JWT.io without explicitly specifying the base64urlencoding)
Running linux on heroku get this build error, runs fine on mac.
createClaimsfT6userIdSS_GSaP3JWT5Claim__: error: undefined reference to '_TFE3JWTPS_13SecondsBackedCfT4dateV10Foundation4Date_x'
I just got the error message that I don't have openssl/conf.h
and others.
Would be nice to have a way to create a key from a certificate containing a public key. Its not easily doable as of now because the initializes of the RSxxx
classes require Bytes
input. The following code is possible with #45
extension RSASigner {
init(certificate: String) throws {
let certificateBytes = certificate.bytes
let cert_bio = BIO_new_mem_buf(certificateBytes, Int32(certificateBytes.count))
var _cert: UnsafeMutablePointer<X509>?
PEM_read_bio_X509(cert_bio, &_cert, nil, nil)
guard let cert = _cert else {
throw Abort.custom(status: .internalServerError, message: "Failed to decode certificate.")
}
guard let publicKey = X509_get_pubkey(cert) else {
throw Abort.custom(status: .internalServerError, message: "Failed to decode public key.")
}
guard let rsa = EVP_PKEY_get1_RSA(publicKey) else {
throw Abort.custom(status: .internalServerError, message: "Failed to get rsa key.")
}
self.init(key: .public(rsa))
}
}
Here's repro code from user, this will not work, if you remove headers, it will. This can be used to create a repro case and fix:
let expiry = Date() + 1800
let headers: JSON = JSON(["aaaa": "bbbb", "cccc": 1])
let payload = JSON([
ExpirationTimeClaim(date: expiry)
])
let jwt = try JWT(headers: headers, payload: payload, signer: HS512(key: "secret".bytes))
let token = try jwt.createToken()
let receivedJWT = try JWT(token: token)
do {
try receivedJWT.verifySignature(using: HS512(key: "secret".bytes))
try receivedJWT.verifyClaims([ExpirationTimeClaim(date: Date())])
}
catch {
print(error)
}
Currently when verifying a token the message is rebuilt from the header and payload nodes via createMessage(). This only produces the correct message when the original JSON is ordered and minified in the exact same way, otherwise a malformed message is produced and verification will fail.
Example (https://jwt.io)
Source Header:
{
"alg": "HS256",
"typ": "JWT"
}
Source Payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Source Header Minified:
{"alg":"HS256","typ":"JWT"}
Source Payload Minified:
{"sub":"1234567890","name":"John Doe","admin":true}
Source Message:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
VaporJWT Reconstructed Header:
{"alg":"HS256","typ":"JWT"}
VaporJWT Reconstructed Payload: (different than source)
{"admin":true,"name":"John Doe","sub":"1234567890"}
VaporJWT Reconstructed Message: (different than source)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMTIzNDU2Nzg5MCJ9
As per the discord discussions, logging this here.
There's lots of good and relevant code within JWT, but it's not possible to reuse some key pieces when working with another provider. For example, I'd think it would be nice if you could examine the Apple+JWT.swift
or Google+JWT.swift
and then roll your own verifier that extends Request.JWT
and Application.JWT
.
If you try to do this though, you'll get inaccessible due to internal protection for some pieces.
import Vapor
import JWT
extension Request.JWT {
public var cognito: Cognito{
.init(request: self.request)
}
public struct Cognito{
let request: Request
...
I believe the same will happen with regards to the application
property on the extension of Application.JWT
.
I used vapor-jwt run heroku have no problem. but I used in Ubuntu 14.04.2 run error.
information:
execute this line.
let jwt = try JWT(payload: Node(ExpirationTimeClaim(Date() + 60)), signer: HS256(key: "secret"))
*** Error in `.build/debug/App': free(): invalid next size (fast): 0x00007f3544013fe0 ***
I can't seem to get the library to create a JWT using the RS256 signer. I have some key that looks like this:
-----BEGIN PRIVATE KEY-----\nMIIEvA......a5efqA==\n-----END PRIVATE KEY-----\n
First, I removed the beginning and ending tags, and the \n's, so I have just the base64-key: MIIEvA...a5efqA==
as a string. I then used the .bytes
method (that was used in the playground example) to convert it to the correct type, and initialized the signer like so:
let signer = try RS256(key: key.bytes)
However, this throws an error: "JWT error: Could not create key". Why is this? I tried using Data and Array to convert the string to [UInt8] as well, but that gave the same error. Is there a better way to create the signer object without doing the type conversion myself?
Thank you!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.