Skip to content

Commit ca0e344

Browse files
authored
[Examples] Auth middlewares: client and server (#440)
[Examples] Auth middlewares: client and server ### Motivation Show auth client and server middlewares. ### Modifications Added those two examples. ### Result Auth examples in place. ### Test Plan Tested manually together. Reviewed by: simonjbeaumont Builds: ✔︎ pull request validation (5.10) - Build finished. ✔︎ pull request validation (5.9) - Build finished. ✔︎ pull request validation (5.9.0) - Build finished. ✔︎ pull request validation (compatibility test) - Build finished. ✔︎ pull request validation (docc test) - Build finished. ✔︎ pull request validation (examples) - Build finished. ✔︎ pull request validation (integration test) - Build finished. ✔︎ pull request validation (nightly) - Build finished. ✔︎ pull request validation (soundness) - Build finished. #440
1 parent b82666d commit ca0e344

File tree

32 files changed

+476
-6
lines changed

32 files changed

+476
-6
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.DS_Store
2+
.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.vscode
9+
/Package.resolved
10+
.ci/
11+
.docc-build/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// swift-tools-version:5.9
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// This source file is part of the SwiftOpenAPIGenerator open source project
5+
//
6+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
import PackageDescription
16+
17+
let package = Package(
18+
name: "AuthenticationClientMiddleware",
19+
platforms: [.macOS(.v11), .iOS(.v14), .tvOS(.v14), .watchOS(.v7), .visionOS(.v1)],
20+
dependencies: [
21+
.package(url: "https://github.com/apple/swift-openapi-generator", exact: "1.0.0-alpha.1"),
22+
.package(url: "https://github.com/apple/swift-openapi-runtime", exact: "1.0.0-alpha.1"),
23+
.package(url: "https://github.com/apple/swift-openapi-urlsession", exact: "1.0.0-alpha.1"),
24+
.package(url: "https://github.com/apple/swift-http-types", from: "1.0.0"),
25+
],
26+
targets: [
27+
.target(
28+
name: "AuthenticationClientMiddleware",
29+
dependencies: [
30+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
31+
.product(name: "HTTPTypes", package: "swift-http-types"),
32+
]
33+
),
34+
.executableTarget(
35+
name: "HelloWorldURLSessionClient",
36+
dependencies: [
37+
"AuthenticationClientMiddleware", .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
38+
.product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession"),
39+
],
40+
plugins: [.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator")]
41+
),
42+
]
43+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Client Authentication Middleware
2+
3+
In this example we'll implement a `ClientMiddleware` that injects an authentication header into the request.
4+
5+
## Overview
6+
7+
This example extends the [HelloWorldURLSessionClient](../HelloWorldURLSessionClient)
8+
with a new target, `AuthenticationClientMiddleware`, which is then used when creating
9+
the `Client`.
10+
11+
NOTE: This example shows just one way of injecting authentication information in a middleware
12+
and is purely for illustrative purposes.
13+
14+
The tool uses the [URLSession](https://developer.apple.com/documentation/foundation/urlsession) API to perform the HTTP call, wrapped in the [Swift OpenAPI URLSession Transport](https://github.com/apple/swift-openapi-urlsession).
15+
16+
The server can be started by running the `AuthenticationServerMiddleware` example locally.
17+
18+
## Usage
19+
20+
Build and run the client CLI using:
21+
22+
```
23+
$ swift run HelloWorldURLSessionClient token_for_Fr
24+
Hello, Stranger! (Requested by: Frank)
25+
$ swift run HelloWorldURLSessionClient invalid_token
26+
Unauthorized
27+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import OpenAPIRuntime
15+
import Foundation
16+
import HTTPTypes
17+
18+
/// A client middleware that injects a value into the `Authorization` header field of the request.
19+
package struct AuthenticationMiddleware {
20+
21+
/// The value for the `Authorization` header field.
22+
private let value: String
23+
24+
/// Creates a new middleware.
25+
/// - Parameter value: The value for the `Authorization` header field.
26+
package init(authorizationHeaderFieldValue value: String) { self.value = value }
27+
}
28+
29+
extension AuthenticationMiddleware: ClientMiddleware {
30+
package func intercept(
31+
_ request: HTTPRequest,
32+
body: HTTPBody?,
33+
baseURL: URL,
34+
operationID: String,
35+
next: (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
36+
) async throws -> (HTTPResponse, HTTPBody?) {
37+
var request = request
38+
// Adds the `Authorization` header field with the provided value.
39+
request.headerFields[.authorization] = value
40+
return try await next(request, body, baseURL)
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftOpenAPIGenerator open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import OpenAPIURLSession
15+
import Foundation
16+
import AuthenticationClientMiddleware
17+
18+
@main struct HelloWorldURLSessionClient {
19+
static func main() async throws {
20+
let args = CommandLine.arguments
21+
guard args.count == 2 else {
22+
print("Requires a token")
23+
exit(1)
24+
}
25+
let client = Client(
26+
serverURL: URL(string: "http://localhost:8080/api")!,
27+
transport: URLSessionTransport(),
28+
middlewares: [AuthenticationMiddleware(authorizationHeaderFieldValue: args[1])]
29+
)
30+
let response = try await client.getGreeting()
31+
switch response {
32+
case .ok(let okResponse): print(try okResponse.body.json.message)
33+
case .unauthorized: print("Unauthorized")
34+
case .undocumented(statusCode: let statusCode, _): print("Undocumented status code: \(statusCode)")
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
generate:
2+
- types
3+
- client
4+
accessModifier: internal
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
openapi: '3.1.0'
2+
info:
3+
title: GreetingService
4+
version: 1.0.0
5+
servers:
6+
- url: https://example.com/api
7+
description: Example service deployment.
8+
paths:
9+
/greet:
10+
get:
11+
operationId: getGreeting
12+
parameters:
13+
- name: name
14+
required: false
15+
in: query
16+
description: The name used in the returned greeting.
17+
schema:
18+
type: string
19+
responses:
20+
'200':
21+
description: A success response with a greeting.
22+
content:
23+
application/json:
24+
schema:
25+
$ref: '#/components/schemas/Greeting'
26+
'401':
27+
description: Authentication required.
28+
components:
29+
schemas:
30+
Greeting:
31+
type: object
32+
description: A value with the greeting contents.
33+
properties:
34+
message:
35+
type: string
36+
description: The string representation of the greeting.
37+
required:
38+
- message
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.DS_Store
2+
.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.vscode
9+
/Package.resolved
10+
.ci/
11+
.docc-build/
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// swift-tools-version:5.9
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// This source file is part of the SwiftOpenAPIGenerator open source project
5+
//
6+
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
import PackageDescription
16+
17+
let package = Package(
18+
name: "AuthenticationServerMiddleware",
19+
platforms: [.macOS(.v10_15)],
20+
dependencies: [
21+
.package(url: "https://github.com/apple/swift-openapi-generator", exact: "1.0.0-alpha.1"),
22+
.package(url: "https://github.com/apple/swift-openapi-runtime", exact: "1.0.0-alpha.1"),
23+
.package(url: "https://github.com/swift-server/swift-openapi-vapor", exact: "1.0.0-alpha.1"),
24+
.package(url: "https://github.com/vapor/vapor", from: "4.87.1"),
25+
],
26+
targets: [
27+
.target(
28+
name: "AuthenticationServerMiddleware",
29+
dependencies: [.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime")]
30+
),
31+
.executableTarget(
32+
name: "HelloWorldVaporServer",
33+
dependencies: [
34+
"AuthenticationServerMiddleware", .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
35+
.product(name: "OpenAPIVapor", package: "swift-openapi-vapor"),
36+
.product(name: "Vapor", package: "vapor"),
37+
],
38+
plugins: [.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator")]
39+
),
40+
]
41+
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Server Authentication Server
2+
3+
In this example we'll implement a `ServerMiddleware` that verifies an authentication header in the request.
4+
5+
## Overview
6+
7+
This example extends the [HelloWorldVaporServer](../HelloWorldVaporServer) with a new target, `AuthenticationServerMiddleware`, which is then used when registering the server handler with the transport.
8+
9+
NOTE: This example shows just one way of varifying authentication information in a middleware and is purely for illustrative purposes.
10+
11+
The tool uses the [Vapor](https://github.com/vapor/vapor) server framework to handle HTTP requests, wrapped in the [Swift OpenAPI Vapor Transport](https://github.com/swift-server/swift-openapi-vapor).
12+
13+
The CLI starts the server on `http://localhost:8080` and can be invoked by running the `AuthenticationClientMiddleware` example client or on the command line using:
14+
15+
```
16+
$ curl -H 'Authorization: token_for_Frank' 'http://localhost:8080/api/greet?name=Jane'
17+
{
18+
"message" : "Hello, Jane! (Requested by: Frank)"
19+
}
20+
```
21+
22+
## Usage
23+
24+
Build and run the server CLI using:
25+
26+
```
27+
$ swift run
28+
2023-12-01T14:14:35+0100 notice codes.vapor.application : [Vapor] Server starting on http://127.0.0.1:8080
29+
...
30+
```

0 commit comments

Comments
 (0)