Skip to content

Commit

Permalink
feat: add jwt support (#9)
Browse files Browse the repository at this point in the history
* feat: add jwt support for services

* feat: add jwt token support
  • Loading branch information
sirily11 authored Jul 18, 2022
1 parent d55b10b commit c309647
Show file tree
Hide file tree
Showing 21 changed files with 308 additions and 75 deletions.
1 change: 1 addition & 0 deletions functions/authentication/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ["<rootDir>/setup.ts"],
};
5 changes: 3 additions & 2 deletions functions/authentication/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"@types/jest": "^28.1.6",
"jest": "^28.1.3",
"ts-jest": "^28.0.7",
"wrangler": "2.0.22",
"typescript": "4.7.4"
"typescript": "4.7.4",
"wrangler": "2.0.22"
},
"private": true,
"scripts": {
Expand All @@ -16,6 +16,7 @@
"test": "jest"
},
"dependencies": {
"@tsndr/cloudflare-worker-jwt": "^2.0.1",
"ethers": "^5.6.9"
}
}
6 changes: 6 additions & 0 deletions functions/authentication/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions functions/authentication/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as crypto from "crypto";

//@ts-ignore
global.crypto = {
subtle: crypto.webcrypto.subtle,
};
13 changes: 9 additions & 4 deletions functions/authentication/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import { handler, Message } from ".";
import { ethers } from "ethers";

describe("Given a handler", () => {
test("When authentication failed", () => {
beforeAll(() => {
process.env.PASSWORD = "password";
});

test("When authentication failed", async () => {
const message: Message = {
message: "Hello World",
signature: "0x",
address: "0x0000000000000000000000000000000000000000",
};
const response = handler(message);
const response = await handler(message);
expect(response.statusCode).toBe(403);
});

Expand All @@ -22,8 +26,9 @@ describe("Given a handler", () => {
signature: signature,
address: wallet.address,
};
const response = handler(message);
const response = await handler(message);
expect(response.statusCode).toBe(200);
let body = JSON.parse(response.body);
});

test("When authentication failed", async () => {
Expand All @@ -36,7 +41,7 @@ describe("Given a handler", () => {
signature: signature,
address: wallet.address,
};
const response = handler(message);
const response = await handler(message);
expect(response.statusCode).toBe(403);
});
});
24 changes: 17 additions & 7 deletions functions/authentication/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
import { ethers } from "ethers";
import jwt from "@tsndr/cloudflare-worker-jwt";

export interface Message {
message: string;
signature: string;
address: string;
}

interface JWTPayload {
userId: string;
}

interface HandlerResponse {
statusCode: number;
body: string;
}

export function handler({
export async function handler({
message,
signature,
address,
}: Message): HandlerResponse {
}: Message): Promise<HandlerResponse> {
try {
const signedAddress = ethers.utils.verifyMessage(message, signature);
if (address === signedAddress) {
let password = process.env.PASSWORD!;
const payload: JWTPayload = {
userId: address,
};
let token = await jwt.sign(payload, password);
return {
statusCode: 200,
body: JSON.stringify({
message: "OK",
address: address,
signature: signature,
accessToken: token,
}),
};
}
Expand All @@ -34,19 +43,20 @@ export function handler({
statusCode: 403,
body: JSON.stringify({
message: "Forbidden",
address: address,
signature: signature,
}),
};
}

export default {
async fetch(request: Request): Promise<Response> {
const message = await request.json<Message>();
const response = handler(message);
const response = await handler(message);

return new Response(response.body, {
status: response.statusCode,
headers: {
"Content-Type": "application/json",
},
});
},
};
18 changes: 18 additions & 0 deletions packages/common/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@
"version" : "1.0.2"
}
},
{
"identity" : "jwt",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vapor/jwt.git",
"state" : {
"revision" : "506d238a707c0e7c1d2cf690863902eaf3bc4e94",
"version" : "4.2.1"
}
},
{
"identity" : "jwt-kit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vapor/jwt-kit.git",
"state" : {
"revision" : "aa7d9cd805b10c1ca17dfc53062ffbc695469cf4",
"version" : "4.6.1"
}
},
{
"identity" : "mongokitten",
"kind" : "remoteSourceControl",
Expand Down
2 changes: 2 additions & 0 deletions packages/common/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ let package = Package(
.package(url: "https://github.com/soto-project/soto", from: "6.0.0"),
.package(url: "https://github.com/swift-server-community/mqtt-nio", from: "2.6.0"),
.package(url: "https://github.com/vapor/fluent-mongo-driver.git", from: "1.0.0"),
.package(url: "https://github.com/vapor/jwt.git", from: "4.0.0"),
],
targets: [
.target(
Expand All @@ -32,6 +33,7 @@ let package = Package(
.product(name: "MQTTNIO", package: "mqtt-nio"),
.product(name: "SotoS3", package: "soto"),
.product(name: "FluentMongoDriver", package: "fluent-mongo-driver"),
.product(name: "JWT", package: "jwt")
]
),
.testTarget(
Expand Down
4 changes: 4 additions & 0 deletions packages/common/Sources/common/Constants/Environments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ public let ENVIRONMENT_S3_ENDPOINT = "ENDPOINT"
public let ENVIRONMENT_S3_REGION = "REGION"
public let ENVIRONMENT_S3_BUCKET_NAME = "BUCKET_NAME"
public let ENVIRONMENT_TRANSCODING_URL = "TRANSCODING_URL"
/**
JWT Password
*/
public let ENVIRONMENT_PASSWORD = "PASSWORD"
31 changes: 31 additions & 0 deletions packages/common/Sources/common/Middlewares/JWTMiddleWare.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// File.swift
//
//
// Created by Qiwei Li on 7/18/22.
//

import Foundation
import Vapor
import JWT
import JWTKit


public class JWTMiddleWare: AsyncMiddleware {
public init(){

}

public func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response {
guard let token = request.headers.bearerAuthorization else {
throw Abort(.unauthorized, reason: "No access token found in header")
}
do {
try request.jwt.verify(token.token, as: JWTVerificationPayload.self)

} catch let error as JWTError {
throw Abort(.unauthorized, reason: error.reason)
}
return try await next.respond(to: request)
}
}
32 changes: 32 additions & 0 deletions packages/common/Sources/common/Types/JWTPayload.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// File.swift
//
//
// Created by Qiwei Li on 7/18/22.
//

import Foundation
import JWT

public struct JWTVerificationPayload: 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 userId = "userId"
}

// The "sub" (subject) claim identifies the principal that is the
// subject of the JWT.
var subject: SubjectClaim

var expiration: ExpirationClaim

// UserId
var userId: String

public func verify(using signer: JWTSigner) throws {
try self.expiration.verifyNotExpired()
}
}
43 changes: 0 additions & 43 deletions packages/common/Sources/common/Utils/InitializeHealper.swift

This file was deleted.

64 changes: 64 additions & 0 deletions packages/common/Sources/common/Utils/InitializeHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Vapor
import Fluent
import SotoS3
import FluentMongoDriver
import MQTTNIO
import JWT


public class InitializeHelper {
let app: Application

public init(app: Application) {
self.app = app
}

// configures your application
public func initializeDB() throws {
try app.databases.use(.mongo(
connectionString: Environment.get(ENVIRONMENT_DB_KEY)!
), as: .mongo)
}

public func initializeAWS() throws {
let accessKey = Environment.get(ENVIRONMENT_ACCESS_KEY)!
let secretKey = Environment.get(ENVIRONMENT_SECRET_KEY)!
let endpoint = Environment.get(ENVIRONMENT_S3_ENDPOINT)!
let region = Environment.get(ENVIRONMENT_S3_REGION)!

var configuration = HTTPClient.Configuration()
configuration.httpVersion = .http1Only

app.aws.client = AWSClient(
credentialProvider: .static(accessKeyId: accessKey, secretAccessKey: secretKey),
httpClientProvider: .shared(HTTPClient(
eventLoopGroupProvider: .createNew,
configuration: configuration
))
)
app.aws.s3 = S3(client: app.aws.client, region: .other(region), endpoint: endpoint)
}

public func initializeMQTT() throws {
let client = MQTTClient(
host: "localhost",
port: 1883,
identifier: "Client",
eventLoopGroupProvider: .createNew,
configuration: .init(userName: "user", password: "password")
)
_ = try client.connect().wait()
app.mqtt.client = client
}

public func initializeJWT() throws {
app.jwt.signers.use(.hs256(key: Environment.get(ENVIRONMENT_PASSWORD)!))
}

}






Loading

0 comments on commit c309647

Please sign in to comment.