Skip to content

Commit 99fa452

Browse files
committed
add an authorization middleware to the example
1 parent 1ae1d4d commit 99fa452

File tree

8 files changed

+96
-21
lines changed

8 files changed

+96
-21
lines changed

Examples/quoteapi/Sources/LambdaAuthorizer/main.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,23 @@ let simpleAuthorizerHandler:
7777

7878
context.logger.debug("+++ Simple Authorizer called +++")
7979

80-
guard let authToken = request.headers["authorization"],
81-
authToken == "Bearer 123"
80+
guard let authToken = request.headers["authorization"]
8281
else {
83-
context.logger.warning("Missing or invalid Authorization header")
82+
context.logger.warning("Missing Authorization header")
8483
return .init(isAuthorized: false, context: [:])
8584
}
8685

86+
// do not take an authorization decision here.
87+
// bring the token to the OpenAPI service and let the developer
88+
// verify authorization there.
89+
8790
return APIGatewayLambdaAuthorizerSimpleResponse(
8891
// this is the authorization decision: yes or no
8992
isAuthorized: true,
9093

9194
// this is additional context we want to return to the caller
92-
context: ["abc1": "xyz1"]
95+
// these values can be retrieved in requestContext.authorizer of the APIGatewayv2 request
96+
context: ["token": authToken]
9397
)
9498
}
9599

Sources/Middleware/AuthenticateUserMiddleware.swift renamed to Examples/quoteapi/Sources/QuoteAPI/AuthenticateUserMiddleware.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ extension AuthenticationServerMiddleware: ServerMiddleware {
6767
next: @Sendable (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (HTTPResponse, HTTPBody?)
6868
) async throws -> (HTTPResponse, HTTPBody?) {
6969
// Extracts the `Authorization` value, if present.
70+
// Even if when we use a Lambda authorizer, the original authorization header is forwarded
7071
// If no `Authorization` header field value was provided, no User is injected into
7172
// the task local.
7273
guard let authorizationHeaderFieldValue = request.headerFields[.authorization] else {
7374
return try await next(request, body, metadata)
7475
}
76+
7577
// Delegate the authentication logic to the closure.
7678
let user = authenticate(authorizationHeaderFieldValue)
7779
// Inject the authenticated user into the task local and call the next middleware.
7880
return try await User.$current.withValue(user) { try await next(request, body, metadata) }
7981
}
80-
}
82+
}

Examples/quoteapi/Sources/QuoteAPI/QuoteService.swift

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,23 @@ struct QuoteServiceImpl: APIProtocol, OpenAPILambdaHttpApi {
2424

2525
let logger: Logger
2626

27-
func register(transport: OpenAPILambdaTransport, middlewares: [ServerMiddleware]) throws {
27+
func register(transport: OpenAPILambdaTransport) throws {
28+
2829
// you have a chance here to customize the routes, for example
2930
try transport.router.get("/health") { _, _ in
3031
"OK"
3132
}
33+
logger.trace("Available Routes\n\(transport.router)") // print the router tree (for debugging purposes)
3234

35+
// to log all request and their response, add a logging middleware
3336
let loggingMiddleware = LoggingMiddleware(logger: logger)
34-
try self.registerHandlers(on: transport, middlewares: middlewares + [loggingMiddleware])
3537

36-
logger.trace("Available Routes\n\(transport.router)") // print the router tree (for debugging purposes)
38+
// This app includes a sample authorization middleware
39+
// It transforms the bearer token into a username.
40+
// The user can be access through a TaskLocal variable.
41+
let authenticationMiddleware = self.authenticationMiddleware()
42+
43+
try self.registerHandlers(on: transport, middlewares: [loggingMiddleware, authenticationMiddleware])
3744
}
3845

3946
static func main() async throws {
@@ -93,6 +100,10 @@ struct QuoteServiceImpl: APIProtocol, OpenAPILambdaHttpApi {
93100

94101
func getQuote(_ input: Operations.getQuote.Input) async throws -> Operations.getQuote.Output {
95102

103+
// You can log events to the AWS Lambda logs here
104+
guard let user = AuthenticationServerMiddleware.User.current else { return .unauthorized(.init()) }
105+
logger.trace("GetQuote for \(user) - Started")
106+
96107
let symbol = input.path.symbol
97108

98109
var date: Date = Date()
@@ -111,6 +122,31 @@ struct QuoteServiceImpl: APIProtocol, OpenAPILambdaHttpApi {
111122
timestamp: date
112123
)
113124

125+
logger.trace("GetQuote - Returning")
126+
114127
return .ok(.init(body: .json(price)))
115128
}
129+
130+
func authenticationMiddleware() -> AuthenticationServerMiddleware {
131+
AuthenticationServerMiddleware(authenticate: { stringValue in
132+
// Warning: this is an overly simplified authentication strategy, checking
133+
// for well-known tokens.
134+
//
135+
// In your project, here you would likely call out to a library that performs
136+
// a cryptographic validation, or similar.
137+
//
138+
// The code is for illustrative purposes only and should not be used directly.
139+
switch stringValue {
140+
case "123":
141+
// A known user authenticated.
142+
return .init(name: "Seb")
143+
case "456":
144+
// A known user authenticated.
145+
return .init(name: "Nata")
146+
default:
147+
// Unknown credentials, no authenticated user.
148+
return nil
149+
}
150+
})
151+
}
116152
}

Examples/quoteapi/Sources/QuoteAPI/openapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,7 @@ paths:
5050
$ref: '#/components/schemas/quote'
5151
400:
5252
description: Bad Request
53+
401:
54+
description: Authentication required
5355
404:
5456
description: Not Found

Examples/quoteapi/events/404.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"rawQueryString": "",
3+
"headers": {
4+
"host": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
5+
"x-forwarded-port": "443",
6+
"content-length": "0",
7+
"x-amzn-trace-id": "Root=1-6571d134-63dbe8ee21efa87555d59265",
8+
"x-forwarded-for": "191.95.148.219",
9+
"x-forwarded-proto": "https",
10+
"accept": "*/*",
11+
"user-agent": "curl/8.1.2"
12+
},
13+
"requestContext": {
14+
"apiId": "b2k1t8fon7",
15+
"http": {
16+
"sourceIp": "191.95.148.219",
17+
"userAgent": "curl/8.1.2",
18+
"method": "GET",
19+
"path": "/undefined",
20+
"protocol": "HTTP/1.1"
21+
},
22+
"timeEpoch": 1701957940365,
23+
"domainPrefix": "b2k1t8fon7",
24+
"accountId": "486652066693",
25+
"time": "07/Dec/2023:14:05:40 +0000",
26+
"stage": "$default",
27+
"domainName": "b2k1t8fon7.execute-api.us-east-1.amazonaws.com",
28+
"requestId": "Pk2gOia2IAMEPOw="
29+
},
30+
"isBase64Encoded": false,
31+
"version": "2.0",
32+
"routeKey": "$default",
33+
"rawPath": "/undefined"
34+
}

Examples/quoteapi/events/GetQuote.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"x-forwarded-for": "191.95.148.219",
99
"x-forwarded-proto": "https",
1010
"accept": "*/*",
11-
"user-agent": "curl/8.1.2"
11+
"user-agent": "curl/8.1.2",
12+
"authorization": "Bearer 123"
1213
},
1314
"requestContext": {
1415
"apiId": "b2k1t8fon7",
@@ -19,6 +20,11 @@
1920
"path": "/stocks/AAPL",
2021
"protocol": "HTTP/1.1"
2122
},
23+
"authorizer": {
24+
"lambda": {
25+
"abc1": "xyz1"
26+
}
27+
},
2228
"timeEpoch": 1701957940365,
2329
"domainPrefix": "b2k1t8fon7",
2430
"accountId": "486652066693",

Sources/OpenAPILambdaHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public struct OpenAPILambdaHandler<OALS: OpenAPILambdaService>: LambdaHandler, S
3939
public init(withService openAPILambdaService: OALS) throws {
4040
self.openAPIService = openAPILambdaService
4141
self.transport = OpenAPILambdaTransport(router: TrieRouter())
42-
try self.openAPIService.register(transport: self.transport, middlewares: [])
42+
try self.openAPIService.register(transport: self.transport)
4343
}
4444

4545
/// The Lambda handling method.

Sources/OpenAPILambdaService.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public protocol OpenAPILambdaService: Sendable {
2727
/// Injects the transport.
2828
///
2929
/// This is where your OpenAPILambdaService implementation must register the transport
30-
func register(transport: OpenAPILambdaTransport, middlewares: [ServerMiddleware]) throws
31-
30+
func register(transport: OpenAPILambdaTransport) throws
31+
3232
/// Convert from `Event` type to `OpenAPILambdaRequest`
3333
/// - Parameters:
3434
/// - context: Lambda context
@@ -55,13 +55,4 @@ extension OpenAPILambdaService {
5555
}
5656
}
5757

58-
// extension OpenAPILambdaService {
5958

60-
// /// Injects the transport.
61-
// /// This is a convenience method that provides an empty middleware list by default
62-
// ///
63-
// /// This is where your OpenAPILambdaService implementation must register the transport
64-
// public func register(transport: OpenAPILambdaTransport, middleware: [ServerMiddleware] = []) throws {
65-
// try register(transport: transport, middleware: middleware)
66-
// }
67-
// }

0 commit comments

Comments
 (0)