Skip to content

Commit

Permalink
fix: inject auth info to appsync handshake headers
Browse files Browse the repository at this point in the history
  • Loading branch information
5d committed Sep 20, 2024
1 parent 6936a28 commit f4b723c
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 113 deletions.
18 changes: 0 additions & 18 deletions Sources/AWSAppSyncApolloExtensions/Utilities/Base64Encoder.swift

This file was deleted.

14 changes: 2 additions & 12 deletions Sources/AWSAppSyncApolloExtensions/Utilities/EndpointHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func appSyncApiEndpoint(_ url: URL) -> URL {
}

urlComponents.host = host.replacingOccurrences(of: "appsync-realtime-api", with: "appsync-api")
urlComponents.scheme = "https"
guard let apiUrl = urlComponents.url else {
return url
}
Expand All @@ -44,20 +45,9 @@ func appSyncRealTimeEndpoint(_ url: URL) -> URL {
}

urlComponents.host = host.replacingOccurrences(of: "appsync-api", with: "appsync-realtime-api")
urlComponents.scheme = "wss"
guard let realTimeUrl = urlComponents.url else {
return url
}

return realTimeUrl
}

func useWebSocketProtocolScheme(url: URL) -> URL {
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return url
}
if urlComponents.scheme == "ws" || urlComponents.scheme == "wss" {
return url
}
urlComponents.scheme = urlComponents.scheme == "http" ? "ws" : "wss"
return urlComponents.url ?? url
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import Foundation

public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient, URLSessionDelegate {

public enum Error: Swift.Error {
case emptyEndpoint
}

private static let jsonEncoder = JSONEncoder()

// MARK: - ApolloWebSocket.WebSocketClient
Expand All @@ -30,7 +34,6 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient,
// MARK: - Internal

private let taskQueue: TaskQueue<Void>
private let endpointURL: URL

/// The underlying URLSessionWebSocketTask
private var connection: URLSessionWebSocketTask? {
Expand Down Expand Up @@ -72,13 +75,11 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient,
callbackQueue: DispatchQueue,
authorizer: AppSyncAuthorizer
) {
self.endpointURL = useWebSocketProtocolScheme(url: appSyncRealTimeEndpoint(endpointURL))
self.request = URLRequest(url: self.endpointURL)
self.request = URLRequest(url: appSyncRealTimeEndpoint(endpointURL))
self.delegate = delegate
self.callbackQueue = callbackQueue
self.authorizer = authorizer
self.taskQueue = TaskQueue()
request.setValue("graphql-ws", forHTTPHeaderField: "Sec-WebSocket-Protocol")
}

public func connect() {
Expand Down Expand Up @@ -160,27 +161,17 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient,
// MARK: - Connect Internals

private func createWebSocketConnection() async throws -> URLSessionWebSocketTask {
let host = appSyncApiEndpoint(endpointURL).host!
var headers = ["host": host]
guard let url = request.url else {
throw Error.emptyEndpoint
}

let authHeaders = try await authorizer.getWebsocketConnectionHeaders(endpoint: endpointURL)
request.setValue("graphql-ws", forHTTPHeaderField: "Sec-WebSocket-Protocol")
request.setValue(appSyncApiEndpoint(url).host, forHTTPHeaderField: "host")
let authHeaders = try await authorizer.getWebsocketConnectionHeaders(endpoint: url)
for authHeader in authHeaders {
headers[authHeader.key] = authHeader.value
request.setValue(authHeader.value, forHTTPHeaderField: authHeader.key)
}

let payload = "{}"

let headerJsonData = try Self.jsonEncoder.encode(headers)
var urlComponents = URLComponents(url: endpointURL, resolvingAgainstBaseURL: false)

urlComponents?.queryItems = [
URLQueryItem(name: "header", value: headerJsonData.base64EncodedString()),
URLQueryItem(name: "payload", value: try? payload.base64EncodedString())
]

let decoratedURL = urlComponents?.url ?? endpointURL
request.url = decoratedURL
AppSyncApolloLogger.debug("[AppSyncWebSocketClient] connecting to server \(decoratedURL)")
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
return urlSession.webSocketTask(with: request)
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ final class EndpointHelperTests: XCTestCase {
let appSyncEndpoint = URL(string: "https://abc.appsync-api.amazonaws.com/graphql")!
XCTAssertEqual(
appSyncRealTimeEndpoint(appSyncEndpoint),
URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql")
URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql")
)
}

func testAppSyncRealTimeEndpoint_withAWSAppSyncRealTimeDomain_returnTheSameDomain() {
let appSyncEndpoint = URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql")!
XCTAssertEqual(
appSyncRealTimeEndpoint(appSyncEndpoint),
URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql")
URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql")
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
218CFDC02C5A8714009D70B9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 218CFDBF2C5A8714009D70B9 /* Preview Assets.xcassets */; };
218CFDF62C5AD23D009D70B9 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDF52C5AD23D009D70B9 /* AWSCognitoAuthPlugin */; };
218CFDF82C5AD23D009D70B9 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDF72C5AD23D009D70B9 /* Amplify */; };
218CFDFA2C5AD408009D70B9 /* amplify_outputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 218CFDF92C5AD408009D70B9 /* amplify_outputs.json */; };
218CFDFD2C5AD4BB009D70B9 /* Authenticator in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDFC2C5AD4BB009D70B9 /* Authenticator */; };
218CFE152C5AD66D009D70B9 /* TodoList.graphql in Resources */ = {isa = PBXBuildFile; fileRef = 218CFE112C5AD66D009D70B9 /* TodoList.graphql */; };
218CFE162C5AD66D009D70B9 /* schema.json in Resources */ = {isa = PBXBuildFile; fileRef = 218CFE122C5AD66D009D70B9 /* schema.json */; };
Expand All @@ -30,6 +29,7 @@
21CFD7C92C76641E0071C70F /* AWSAppSyncApolloExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 21CFD7C82C76641E0071C70F /* AWSAppSyncApolloExtensions */; };
21FDF39C2C62B20200481EA0 /* APIKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FDF39B2C62B20200481EA0 /* APIKeyTests.swift */; };
21FDF39E2C62B3E500481EA0 /* IntegrationTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FDF39D2C62B3E500481EA0 /* IntegrationTestBase.swift */; };
60CE34112C9CDC1700DEAB45 /* amplify_outputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -50,7 +50,6 @@
218CFDBF2C5A8714009D70B9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
218CFDC52C5A8714009D70B9 /* IntegrationTestAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTestAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
218CFDF32C5AD177009D70B9 /* aws-appsync-apollo-interceptors-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "aws-appsync-apollo-interceptors-swift"; path = ../..; sourceTree = "<group>"; };
218CFDF92C5AD408009D70B9 /* amplify_outputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = amplify_outputs.json; sourceTree = "<group>"; };
218CFE112C5AD66D009D70B9 /* TodoList.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; name = TodoList.graphql; path = IntegrationTestApp/graphql/TodoList.graphql; sourceTree = SOURCE_ROOT; };
218CFE122C5AD66D009D70B9 /* schema.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = schema.json; path = IntegrationTestApp/graphql/schema.json; sourceTree = SOURCE_ROOT; };
218CFE132C5AD66D009D70B9 /* SubscribeTodo.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; name = SubscribeTodo.graphql; path = IntegrationTestApp/graphql/SubscribeTodo.graphql; sourceTree = SOURCE_ROOT; };
Expand All @@ -61,6 +60,7 @@
21B8F2D32C6298580042981F /* IAMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAMTests.swift; sourceTree = "<group>"; };
21FDF39B2C62B20200481EA0 /* APIKeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIKeyTests.swift; sourceTree = "<group>"; };
21FDF39D2C62B3E500481EA0 /* IntegrationTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTestBase.swift; sourceTree = "<group>"; };
60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = amplify_outputs.json; path = "../../../../tmp/apollo-extension-integ-test/amplify_outputs.json"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -113,7 +113,7 @@
isa = PBXGroup;
children = (
21B8F2D02C5D8ACF0042981F /* graphql */,
218CFDF92C5AD408009D70B9 /* amplify_outputs.json */,
60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */,
218CFDB82C5A8711009D70B9 /* IntegrationTestAppApp.swift */,
218CFDBA2C5A8711009D70B9 /* ContentView.swift */,
218CFE192C5AD6B1009D70B9 /* Network.swift */,
Expand Down Expand Up @@ -259,7 +259,7 @@
buildActionMask = 2147483647;
files = (
218CFE172C5AD66D009D70B9 /* SubscribeTodo.graphql in Resources */,
218CFDFA2C5AD408009D70B9 /* amplify_outputs.json in Resources */,
60CE34112C9CDC1700DEAB45 /* amplify_outputs.json in Resources */,
218CFE162C5AD66D009D70B9 /* schema.json in Resources */,
218CFDC02C5A8714009D70B9 /* Preview Assets.xcassets in Resources */,
218CFDBD2C5A8714009D70B9 /* Assets.xcassets in Resources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/amplify-swift.git",
"state" : {
"branch" : "lawmicha.apollo",
"revision" : "22ae18b521a1d67deced0d06dcacce0bfabd2165"
"revision" : "36e5e92a5c8e1df92b69c5575de1c1f02ed1611e",
"version" : "2.41.1"
}
},
{
Expand Down Expand Up @@ -41,26 +41,26 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/awslabs/aws-crt-swift",
"state" : {
"revision" : "0d0a0cf2e2cb780ceeceac190b4ede94f4f96902",
"version" : "0.26.0"
"revision" : "7b42e0343f28b3451aab20840dc670abd12790bd",
"version" : "0.36.0"
}
},
{
"identity" : "aws-sdk-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/awslabs/aws-sdk-swift.git",
"state" : {
"revision" : "47922c05dd66be717c7bce424651a534456717b7",
"version" : "0.36.2"
"revision" : "828358a2c39d138325b0f87a2d813f4b972e5f4f",
"version" : "1.0.0"
}
},
{
"identity" : "smithy-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/smithy-lang/smithy-swift",
"state" : {
"revision" : "8a5b0105c1b8a1d26a9435fb0af3959a7f5de578",
"version" : "0.41.1"
"revision" : "0ed3440f8c41e27a0937364d5035d2d4fefb8aa3",
"version" : "0.71.0"
}
},
{
Expand Down
33 changes: 23 additions & 10 deletions Tests/IntegrationTestApp/IntegrationTestAppTests/APIKeyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,23 @@ final class APIKeyTests: IntegrationTestBase {

func testAPIKeyApolloClientMutation() throws {
let completed = expectation(description: "mutation completed")

Network.shared.apolloAPIKey.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in
let todoId = UUID().uuidString
Network.shared.apolloAPIKey.perform(
mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId)))
) { result in
switch result {
case .success(let graphQLResult):
guard (graphQLResult.data?.createTodo) != nil else {
guard let newlyCreatedTodo = graphQLResult.data?.createTodo else {
XCTFail("Missing created Todo")
return
}
if let errors = graphQLResult.errors {
XCTFail("Failed with errors \(errors)")
}
completed.fulfill()

if newlyCreatedTodo.id == todoId {
completed.fulfill()
}
case .failure(let error):
XCTFail("Could not create todo \(error)")
}
Expand Down Expand Up @@ -84,18 +89,21 @@ final class APIKeyTests: IntegrationTestBase {
func testSubscriptionReceivesMutation() async throws {
AppSyncApolloLogger.logLevel = .verbose
let receivedMutation = expectation(description: "received mutation")

let todoId = UUID().uuidString
let activeSubscription = Network.shared.apolloAPIKey.subscribe(subscription: OnCreateSubscription()) { result in
switch result {
case .success(let graphQLResult):
guard (graphQLResult.data?.onCreateTodo) != nil else {
guard let newlyCreatedTodo = graphQLResult.data?.onCreateTodo else {
XCTFail("Missing created Todo")
return
}
if let errors = graphQLResult.errors {
XCTFail("Failed with errors \(errors)")
}
receivedMutation.fulfill()

if newlyCreatedTodo.id == todoId {
receivedMutation.fulfill()
}
case .failure(let error):
XCTFail("Could not create todo \(error)")
}
Expand All @@ -104,17 +112,22 @@ final class APIKeyTests: IntegrationTestBase {
try await Task.sleep(nanoseconds: 5 * 1_000_000_000) // 5 seconds

let completed = expectation(description: "mutation completed")
Network.shared.apolloAPIKey.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in
Network.shared.apolloAPIKey.perform(
mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId)))
) { result in
switch result {
case .success(let graphQLResult):
guard (graphQLResult.data?.createTodo) != nil else {
guard let newlyCreatedTodo = graphQLResult.data?.createTodo else {
XCTFail("Missing created Todo")
return
}
if let errors = graphQLResult.errors {
XCTFail("Failed with errors \(errors)")
}
completed.fulfill()

if newlyCreatedTodo.id == todoId {
completed.fulfill()
}
case .failure(let error):
XCTFail("Could not create todo \(error)")
}
Expand Down
Loading

0 comments on commit f4b723c

Please sign in to comment.