Skip to content

Commit 65b8256

Browse files
committed
wip: add auth header parser
1 parent 01de7e2 commit 65b8256

File tree

7 files changed

+87
-9
lines changed

7 files changed

+87
-9
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ jobs:
6161

6262
- name: Push To Production
6363
run: |
64-
curl --request GET '${{ secrets.DEPLOYMENT_WEBHOOK_URL }}' --header 'Authorization: Bearer ${{ secrets.DEPLOYMENT_TOKEN }}'
64+
curl --request POST '${{ secrets.DEPLOYMENT_WEBHOOK_URL }}' --header 'Authorization: Bearer ${{ secrets.DEPLOYMENT_TOKEN }}'

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let package = Package(
2323
.package(url: "https://github.com/pointfreeco/swift-url-routing.git", exact: "0.6.2"),
2424
.package(url: "https://github.com/pointfreeco/swift-dependencies.git", exact: "1.6.2"),
2525
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.1"),
26-
.package(url: "https://github.com/errorerrorerror/swift-cascadia", revision: "7ecd4f55648087446d194aa07ba0cfbc5e7461a6")
26+
.package(url: "https://github.com/errorerrorerror/swift-cascadia", revision: "a13dfd0a3818c8f9368bbd4aeb3c6607f68838bd")
2727
],
2828
targets: [
2929
.target(

Sources/App/Middlewares/SiteMiddleware.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ struct SiteMiddleware<Context: RequestContext>: RouterController {
2929
case .api(.activity(.all)):
3030
return try JSONEncoder().encode(self.activityClient.activity(), from: req, context: ctx)
3131
case let .api(.activity(.location(location))):
32-
guard let authorization = req.headers[.authorization] else {
32+
guard let auth = req.headers[.authorization].flatMap({ try? Auth.parser.parse($0) }) else {
3333
throw HTTPError(.notFound)
3434
}
35-
35+
try auth.validate()
3636
self.activityClient.updateLocation(location)
3737
return Response(status: .ok)
3838
case let .api(.activity(.nowPlaying(nowPlaying))):
39+
guard let auth = req.headers[.authorization].flatMap({ try? Auth.parser.parse($0) }) else {
40+
throw HTTPError(.notFound)
41+
}
42+
try auth.validate()
3943
self.activityClient.updateNowPlaying(nowPlaying)
4044
return Response(status: .ok)
4145
}

Sources/App/Utils/Auth.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import Hummingbird
2+
import Foundation
3+
import Dependencies
4+
import Parsing
5+
6+
enum Auth: Sendable, Equatable {
7+
/// Token
8+
case bearer(String)
9+
10+
/// base64-encoded credentials
11+
case basic(String, String)
12+
13+
/// sha256-algorithm
14+
case digest(String)
15+
16+
static var parser: some Parser<Substring, Auth> {
17+
OneOf {
18+
Parse(.case(Auth.bearer)) {
19+
"Bearer "
20+
Rest().map(.string)
21+
}
22+
23+
Parse(.case(Auth.basic)) {
24+
"Basic "
25+
26+
Rest().map(Base64SubstringToSubstring()).pipe {
27+
Prefix { $0 != ":" }.map(.string)
28+
":"
29+
Rest().map(.string)
30+
}
31+
}
32+
33+
Parse(.case(Auth.digest)) {
34+
"Digest "
35+
Rest().map(.string)
36+
}
37+
}
38+
}
39+
40+
func validate() throws {
41+
@Dependency(\.envVar) var env
42+
switch self {
43+
case let .basic(username, pass):
44+
if username != env.basicAuth.0 || pass != env.basicAuth.1 {
45+
throw HTTPError(.notFound)
46+
}
47+
default: throw HTTPError(.notFound)
48+
}
49+
}
50+
}
51+
52+
struct Base64SubstringToSubstring: Conversion {
53+
@usableFromInline
54+
init() {}
55+
56+
@inlinable
57+
func apply(_ input: Substring) -> Substring {
58+
Data(base64Encoded: String(input)).flatMap {
59+
String(data: $0, encoding: .utf8)?[...]
60+
} ?? ""
61+
}
62+
63+
@inlinable
64+
func unapply(_ output: Substring) -> Substring {
65+
output.data(using: .utf8)?
66+
.base64EncodedString()[...] ?? ""
67+
}
68+
}

Sources/App/Utils/Environment+.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ public enum AppEnv: String, Codable, ExpressibleByArgument {
99

1010
private enum EnvKeys {
1111
static let appSecret = "APP_SECRET"
12+
static let basicAuthUsername = "BASIC_AUTH_USER"
13+
static let basicAuthPassword = "BASIC_AUTH_PASSWD"
1214
}
1315

1416
extension Environment {
1517
var appSecret: String {
16-
self.get(EnvKeys.appSecret, as: String.self) ?? "deadbeefdeadbeefdeadbeefdeadbeef"
18+
self.get(EnvKeys.appSecret) ?? "deadbeefdeadbeefdeadbeefdeadbeef"
19+
}
20+
21+
var basicAuth: (String, String) {
22+
(self.get(EnvKeys.basicAuthUsername) ?? "dead", self.get(EnvKeys.basicAuthPassword) ?? "beef")
1723
}
1824
}
1925

Sources/Routes/Utils/URLRequestData+Hummingbird.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ public extension URLRequestData {
4444
}
4545
}
4646

47-
private extension HTTPFields {
48-
var basicAuthorization: (String, String)? {
47+
extension HTTPFields {
48+
public var basicAuthorization: (String, String)? {
4949
if let string = self[.authorization] {
5050
let headerParts = string
5151
.split(separator: " ", maxSplits: 1, omittingEmptySubsequences: false)

0 commit comments

Comments
 (0)