Skip to content

fix: inject auth info to appsync handshake headers #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

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 @@ -30,7 +30,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 +71,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 +157,22 @@ 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 {
fatalError("""
Empty endpoint. This should not happen.
Please take a look at https://github.com/aws-amplify/aws-appsync-apollo-extensions-swift/issues
to see if there are any existing issues that match your scenario, and file an issue with
the details of the bug if there isn't.
""")
}

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
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