Keychain adds a complete and customizable user authentication system to your API project.
Update your Package.swift
file.
.package(url: "https://github.com/nodes-vapor/keychain.git", from: "2.0.0")
targets: [
.target(
name: "App",
dependencies: [
...
.product(name: "Keychain", package: "keychain"),
]
),
...
]
These are the steps required to use Keychain in your project.
KeychainPayload
protocolKeychainConfig
objects for the key types you would like to useKeychain
using a Signer
and the KeychainConfig
objects defined in step 2Keychain
Time to look at each step in detail.
Your payload must conform to the KeychainPayload
protocol, meaning that it must contain:
init(expirationDate: Date, user: User) throws
func findUser(request: Request) -> EventLoopFuture<User>
which is where you do a search for the user you were presented in the init
methodfunc verify(using signer: JWTSigner) throws
which will verify that your token is still validFurthermore you need to tell your KeychainPayload
what its associatedtype
for User
translates to.
Here is an example that uses elements from a JWT token and verifies that the expiration (exp
) claim is not expired. Note that findUser
in this case only returns a test user. In real life you probably want to do a lookup somewhere where users are stored.
import JWT
import Keychain
import Vapor
struct UserJWTPayload: KeychainPayload {
let exp: ExpirationClaim
let sub: SubjectClaim
init(expirationDate: Date, user: User) {
self.exp = .init(value: expirationDate)
self.sub = .init(value: user.id)
}
func findUser(request: Request) -> EventLoopFuture<User> {
request.eventLoop.future(request.testUser).unwrap(or: TestError.userNotFound)
}
func verify(using signer: JWTSigner) throws {
try exp.verifyNotExpired()
}
}
KeychainConfig
ObjectsYour KeychainConfig
objects must contain:
jwkIdentifier
expirationTimeInterval
And you need to connect your KeychainConfig
with the KeychainPayload
you defined in step 1 (the KeychainConfig
has a typealias
for a KeychainPayload
).
Here is an example creating three KeychainConfig
objects:
UserAccessKeychainConfig
with the identifier "access" and an expirationTimeInterval
of 300 secondsUserRefreshKeychainConfig
with the identifier "refresh" and an expirationTimeInterval
of 600 secondsUserResetKeychainConfig
with the identifier "reset" and an expirationTimeInterval
of 400 secondsimport JWT
import Keychain
struct UserAccessKeychainConfig: KeychainConfig, Equatable {
typealias JWTPayload = UserJWTPayload
static var jwkIdentifier: JWKIdentifier = "access"
let expirationTimeInterval: TimeInterval = 300
}
struct UserRefreshKeychainConfig: KeychainConfig, Equatable {
typealias JWTPayload = UserJWTPayload
static var jwkIdentifier: JWKIdentifier = "refresh"
let expirationTimeInterval: TimeInterval = 600
}
struct UserResetKeychainConfig: KeychainConfig, Equatable {
typealias JWTPayload = UserJWTPayload
static var jwkIdentifier: JWKIdentifier = "reset"
let expirationTimeInterval: TimeInterval = 400
}
Keychain
Time to tie it all together! In your configure.swift
you can add multiple KeychainConfig
objects as seen here:
app.keychain.configure(
signer: .hs256(key: YourKeyGoesHere...ProbablyReadFromSomeEnvironment),
config: UserAccessKeychainConfig()
)
app.keychain.configure(
signer: JWTSigner(
algorithm: TestJWTAlgorithm(name: UserRefreshKeychainConfig.jwkIdentifier.string)
),
config: UserRefreshKeychainConfig()
)
app.keychain.configure(
signer: JWTSigner(
algorithm: TestJWTAlgorithm(name: UserResetKeychainConfig.jwkIdentifier.string)
),
config: UserResetKeychainConfig()
)
Note the signer
parameter. You can use one of the built-in signers as in the first example where we use the .hs256
signer with a key. Alternatively, you can provide your own signer as it is done in the last two examples.
Keychain
With all the setup out of the way, it is time to kick back and take advantage of Keychain
. You can now use the UserAccessKeychainConfig
, UserRefreshKeychainConfig
and UserResetKeychainConfig
objects that you created previously to generate JWT tokens by calling the makeToken(on:, currentDate:)
Here is an example on how to generate a new refreshToken
.
import Keychain
struct UserController {
let currentDate: () -> Date
...
func refreshToken(request: Request) throws -> Response {
let token = try UserRefreshKeychainConfig.makeToken(on: request, currentDate: currentDate())
// here we encode the token string as JSON but you might include your token in a struct
// conforming to `Content`
let response = Response()
try response.content.encode(token, as: .json)
return response
}
}
This package is developed and maintained by the Vapor team at Monstarlab.
This package is open-sourced software licensed under the MIT license
link |
Stars: 40 |
Last commit: 2 years ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics