impl session tokens
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "d53dfe2770a8b5d4063e71737e72637f59cec754bf4553178a66d10f39bfb1ee",
|
"originHash" : "01eb28d6d5d4bfc205f7c8b2f8a3dc3a3d278b26c75f239cbd1cf26e4482e33d",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "async-http-client",
|
"identity" : "async-http-client",
|
||||||
@@ -55,6 +55,24 @@
|
|||||||
"version" : "4.8.1"
|
"version" : "4.8.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "jwt",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/vapor/jwt.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "af1c59762d70d1065ddbc0d7902ea9b3dacd1a26",
|
||||||
|
"version" : "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "jwt-kit",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/vapor/jwt-kit.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "03f5013f0b547ce43abe45e7e90711303a3e5495",
|
||||||
|
"version" : "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "multipart-kit",
|
"identity" : "multipart-kit",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -127,6 +145,15 @@
|
|||||||
"version" : "1.2.0"
|
"version" : "1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-certificates",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-certificates.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "999fd70c7803da89f3904d635a6815a2a7cd7585",
|
||||||
|
"version" : "1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-collections",
|
"identity" : "swift-collections",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
@@ -15,6 +15,8 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.6.0"),
|
.package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.6.0"),
|
||||||
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
|
||||||
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
|
||||||
|
// JWTs
|
||||||
|
.package(url: "https://github.com/vapor/jwt.git", from: "5.0.0"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
@@ -25,6 +27,7 @@ let package = Package(
|
|||||||
.product(name: "Vapor", package: "vapor"),
|
.product(name: "Vapor", package: "vapor"),
|
||||||
.product(name: "NIOCore", package: "swift-nio"),
|
.product(name: "NIOCore", package: "swift-nio"),
|
||||||
.product(name: "NIOPosix", package: "swift-nio"),
|
.product(name: "NIOPosix", package: "swift-nio"),
|
||||||
|
.product(name: "JWT", package: "jwt"),
|
||||||
],
|
],
|
||||||
swiftSettings: swiftSettings
|
swiftSettings: swiftSettings
|
||||||
),
|
),
|
||||||
|
@@ -74,7 +74,36 @@ struct AuthController: RouteCollection {
|
|||||||
guard let account = account else {
|
guard let account = account else {
|
||||||
return Response(status: .badRequest, body: "{\"error\": \"Bad Request\", \"message\": \"Invalid playerId provided.\"}")
|
return Response(status: .badRequest, body: "{\"error\": \"Bad Request\", \"message\": \"Invalid playerId provided.\"}")
|
||||||
}
|
}
|
||||||
return Response(status: .notImplemented)
|
|
||||||
|
let zatExpiry = Date.now.advanced(by: 43200)
|
||||||
|
let zrtExpiry = Date.now.advanced(by: 2592000)
|
||||||
|
|
||||||
|
let zatTokenJWT = generateToken(accountId: try account.requireID(), expires: zatExpiry, type: .ZAT)
|
||||||
|
let zrtTokenJWT = generateToken(accountId: try account.requireID(), expires: zrtExpiry, type: .ZRT)
|
||||||
|
|
||||||
|
let zatToken = try await req.jwt.sign(zatTokenJWT)
|
||||||
|
let zrtToken = try await req.jwt.sign(zrtTokenJWT)
|
||||||
|
|
||||||
|
let res = LoginDeviceRes(
|
||||||
|
zatExpiryTime: Int(zatExpiry.timeIntervalSince1970) * 1000,
|
||||||
|
zrtExpiryTime: Int(zrtExpiry.timeIntervalSince1970) * 1000,
|
||||||
|
firstLogin: true,
|
||||||
|
externalToken: "",
|
||||||
|
zat: zatToken,
|
||||||
|
zrt: zrtToken,
|
||||||
|
player: Player(
|
||||||
|
idpId: account.idpId,
|
||||||
|
appId: account.appId,
|
||||||
|
playerId: String(try account.requireID()),
|
||||||
|
pushOption: PushOptionResponse(night: "n", player: "n"),
|
||||||
|
regTime: Int(account.regDate.timeIntervalSince1970),
|
||||||
|
idpAlias: idpAlias,
|
||||||
|
firstLoginTime: Int(account.firstLogin.timeIntervalSince1970),
|
||||||
|
status: account.status
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return try await res.encodeResponse(for: req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +163,8 @@ struct LoginDeviceRes: Content {
|
|||||||
struct Player: Content {
|
struct Player: Content {
|
||||||
let idpId: String
|
let idpId: String
|
||||||
let appId: String
|
let appId: String
|
||||||
let lang: String
|
|
||||||
let playerId: String
|
let playerId: String
|
||||||
let agreement: AgreementResponse
|
|
||||||
let pushOption: PushOptionResponse
|
let pushOption: PushOptionResponse
|
||||||
let lastLoginTime: Int
|
|
||||||
let regTime: Int
|
let regTime: Int
|
||||||
let idpAlias: String
|
let idpAlias: String
|
||||||
let firstLoginTime: Int
|
let firstLoginTime: Int
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import Fluent
|
import Fluent
|
||||||
import struct Foundation.UUID
|
|
||||||
|
|
||||||
/// Property wrappers interact poorly with `Sendable` checking, causing a warning for the `@ID` property
|
/// Property wrappers interact poorly with `Sendable` checking, causing a warning for the `@ID` property
|
||||||
/// It is recommended you write your model with sendability checking on and then suppress the warning
|
/// It is recommended you write your model with sendability checking on and then suppress the warning
|
||||||
|
@@ -2,6 +2,7 @@ import NIOSSL
|
|||||||
import Fluent
|
import Fluent
|
||||||
import FluentSQLiteDriver
|
import FluentSQLiteDriver
|
||||||
import Vapor
|
import Vapor
|
||||||
|
import JWT
|
||||||
|
|
||||||
// configures your application
|
// configures your application
|
||||||
public func configure(_ app: Application) async throws {
|
public func configure(_ app: Application) async throws {
|
||||||
@@ -14,6 +15,9 @@ public func configure(_ app: Application) async throws {
|
|||||||
app.http.server.configuration.hostname = "0.0.0.0"
|
app.http.server.configuration.hostname = "0.0.0.0"
|
||||||
app.http.server.configuration.port = 8000
|
app.http.server.configuration.port = 8000
|
||||||
|
|
||||||
|
// JWT
|
||||||
|
await app.jwt.keys.add(hmac: "secret", digestAlgorithm: .sha256)
|
||||||
|
|
||||||
// register routes
|
// register routes
|
||||||
try routes(app)
|
try routes(app)
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,43 @@
|
|||||||
|
import JWT
|
||||||
|
import Fluent
|
||||||
|
import Foundation
|
||||||
|
|
||||||
func generateIdpAlias(appId: String, deviceId: String, serialNo: String) -> String {
|
func generateIdpAlias(appId: String, deviceId: String, serialNo: String) -> String {
|
||||||
return "\(appId):\(deviceId):\(serialNo)"
|
return "\(appId):\(deviceId):\(serialNo)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateToken(accountId: Int, expires: Date, type: SessionType) -> SessionPayload {
|
||||||
|
return SessionPayload(
|
||||||
|
accountId: .init(value: String(accountId)),
|
||||||
|
expiration: .init(value: expires),
|
||||||
|
type: type.rawValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SessionPayload: JWTPayload {
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case accountId = "sub"
|
||||||
|
case expiration = "exp"
|
||||||
|
case type = "type"
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "sub" (subject) claim identifies the principal that is the
|
||||||
|
// subject of the JWT.
|
||||||
|
var accountId: 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 type: Int
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user