diff --git a/Amplify/Core/Configuration/AmplifyOutputsData.swift b/Amplify/Core/Configuration/AmplifyOutputsData.swift index 93a2f60e6a..57e388a3ba 100644 --- a/Amplify/Core/Configuration/AmplifyOutputsData.swift +++ b/Amplify/Core/Configuration/AmplifyOutputsData.swift @@ -251,7 +251,8 @@ public struct AmplifyOutputsData: Codable { public struct AmplifyOutputs { /// A closure that resolves the `AmplifyOutputsData` configuration - let resolveConfiguration: () throws -> AmplifyOutputsData + @_spi(InternalAmplifyConfiguration) + public let resolveConfiguration: () throws -> AmplifyOutputsData /// Resolves configuration with `amplify_outputs.json` in the main bundle. public static let amplifyOutputs: AmplifyOutputs = { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin.swift index 6666cbfadc..f68ee9d335 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin.swift @@ -12,6 +12,7 @@ import AWSPluginsCore import AWSClientRuntime // AWSClientRuntime.CredentialsProviding import ClientRuntime // SdkHttpRequestBuilder import InternalAmplifyCredentials // AmplifyAWSCredentialsProvider() +import AwsCommonRuntimeKit // CommonRuntimeKit.initialize() public final class AWSCognitoAuthPlugin: AWSCognitoAuthPluginBehavior { @@ -61,7 +62,7 @@ public final class AWSCognitoAuthPlugin: AWSCognitoAuthPluginBehavior { } extension AWSCognitoAuthPlugin { - public static func getUserPoolAccessToken() async throws -> String { + public func getUserPoolAccessToken() async throws -> String { let authSession = try await Amplify.Auth.fetchAuthSession() guard let tokenResult = getTokenString(from: authSession) else { let error = AuthError.unknown("Did not receive a valid response from fetchAuthSession for get token.") @@ -74,7 +75,8 @@ extension AWSCognitoAuthPlugin { throw error } } - private static func getTokenString(from authSession: AuthSession) -> Result? { + + private func getTokenString(from authSession: AuthSession) -> Result? { if let result = (authSession as? AuthCognitoTokensProvider)?.getCognitoTokens() { switch result { case .success(let tokens): @@ -85,132 +87,48 @@ extension AWSCognitoAuthPlugin { } return nil } - } +import AWSClientRuntime // AWSClientRuntime.CredentialsProviding +import ClientRuntime // SdkHttpRequestBuilder +import InternalAmplifyCredentials // AmplifyAWSCredentialsProvider() +import AwsCommonRuntimeKit // CommonRuntimeKit.initialize() + extension AWSCognitoAuthPlugin { - public static func signRequest(_ urlRequest: URLRequest, region: String) async throws -> URLRequest? { + + public static func sigV4SignedRequest(_ urlRequest: URLRequest, + region: Swift.String, + credentialsProvider: AWSClientRuntime.CredentialsProviding = AmplifyAWSCredentialsProvider(), + signingName: Swift.String = "appsync", + date: ClientRuntime.Date = Date()) async throws -> URLRequest { let requestBuilder = try createAppSyncSdkHttpRequestBuilder( - urlRequest: urlRequest, - headers: urlRequest.allHTTPHeaderFields, - body: urlRequest.httpBody) - - guard let sdkHttpRequest = try await sigV4SignedRequest( - requestBuilder: requestBuilder, - credentialsProvider: AmplifyAWSCredentialsProvider(), - signingName: "appsync", - signingRegion: region, // region - date: Date() + urlRequest: urlRequest) + CommonRuntimeKit.initialize() + let credentials = try await credentialsProvider.getCredentials() + + let flags = SigningFlags(useDoubleURIEncode: true, + shouldNormalizeURIPath: true, + omitSessionToken: false) + let signedBodyHeader: AWSSignedBodyHeader = .none + let signedBodyValue: AWSSignedBodyValue = .empty + let signingConfig = AWSSigningConfig(credentials: credentials, + signedBodyHeader: signedBodyHeader, + signedBodyValue: signedBodyValue, + flags: flags, + date: date, + service: signingName, + region: region, + signatureType: .requestHeaders, + signingAlgorithm: .sigv4) + + guard let httpRequest = await AWSSigV4Signer.sigV4SignedRequest( + requestBuilder: requestBuilder, + + signingConfig: signingConfig ) else { - //throw APIError.unknown("Unable to sign request", "") - return nil - } - - return setHeaders(from: sdkHttpRequest, to: urlRequest) - } - public struct IAM { - let host: String - let authToken: String - let securityToken: String - let amzDate: String - } - - public static func getAuthHeader(_ endpoint: URL, with payload: Data, region: String) async throws -> (String, String, String, String)? { - guard let host = endpoint.host else { - return nil - } - - /// The process of getting the auth header for an IAM based authentication request is as follows: - /// - /// 1. A request is created with the IAM based auth headers (date, accept, content encoding, content type, and - /// additional headers. - let requestBuilder = SdkHttpRequestBuilder() - .withHost(host) - .withPath(endpoint.path) - .withMethod(.post) - .withPort(443) - .withProtocol(.https) - .withHeader(name: "accept", value: "application/json, text/javascript") - .withHeader(name: "content-encoding", value: "amz-1.0") - .withHeader(name: "Content-Type", value: "application/json; charset=UTF-8") - .withHeader(name: "host", value: host) - .withBody(.data(payload)) - - /// 2. The request is SigV4 signed by using all the available headers on the request. By signing the request, the signature is added to - /// the request headers as authorization and security token. - do { - guard let urlRequest = try await sigV4SignedRequest( - requestBuilder: requestBuilder, - credentialsProvider: AmplifyAWSCredentialsProvider(), - signingName: "appsync", - signingRegion: region, // region - date: Date()) - else { - print("Unable to sign request") - return nil - } - - // TODO: Using long lived credentials without getting a session with security token will fail - // since the session token does not exist on the signed request, and is an empty string. - // Once Amplify.Auth is ready to be integrated, this code path needs to be re-tested. - let headers = urlRequest.headers.headers.reduce([String: String]()) { partialResult, header in - switch header.name.lowercased() { - case "authorization", "x-amz-date", "x-amz-security-token": - guard let headerValue = header.value.first else { - return partialResult - } - return partialResult.merging([header.name.lowercased(): headerValue]) { $1 } - default: - return partialResult - } - } - - return ( - host, - headers["authorization"] ?? "", - headers["x-amz-security-token"] ?? "", - headers["x-amz-date"] ?? "" - ) - } catch { - print("Unable to sign request") - return nil - } - } - - - // Helper - - public static func sigV4SignedRequest(requestBuilder: SdkHttpRequestBuilder, - credentialsProvider: AWSClientRuntime.CredentialsProviding, - signingName: Swift.String, - signingRegion: Swift.String, - date: ClientRuntime.Date) async throws -> SdkHttpRequest? { - do { - let credentials = try await credentialsProvider.getCredentials() - - let flags = SigningFlags(useDoubleURIEncode: true, - shouldNormalizeURIPath: true, - omitSessionToken: false) - let signedBodyHeader: AWSSignedBodyHeader = .none - let signedBodyValue: AWSSignedBodyValue = .empty - let signingConfig = AWSSigningConfig(credentials: credentials, - signedBodyHeader: signedBodyHeader, - signedBodyValue: signedBodyValue, - flags: flags, - date: date, - service: signingName, - region: signingRegion, - signatureType: .requestHeaders, - signingAlgorithm: .sigv4) - - let httpRequest = await AWSSigV4Signer.sigV4SignedRequest( - requestBuilder: requestBuilder, - signingConfig: signingConfig - ) - return httpRequest - } catch let error { - throw AuthError.unknown("Unable to sign request", error) + fatalError("Failed to sign request") } + return setHeaders(from: httpRequest, to: urlRequest) } static func setHeaders(from sdkRequest: SdkHttpRequest, to urlRequest: URLRequest) -> URLRequest { @@ -221,15 +139,13 @@ extension AWSCognitoAuthPlugin { return urlRequest } - static func createAppSyncSdkHttpRequestBuilder(urlRequest: URLRequest, - headers: [String : String]?, - body: Data?) throws -> SdkHttpRequestBuilder { + static func createAppSyncSdkHttpRequestBuilder(urlRequest: URLRequest) throws -> SdkHttpRequestBuilder { guard let url = urlRequest.url else { - throw AuthError.unknown("Could not get url from mutable request", nil) + fatalError("Could not get url from mutable request") } guard let host = url.host else { - throw AuthError.unknown("Could not get host from mutable request", nil) + fatalError("Could not get host from mutable request") } var headers = urlRequest.allHTTPHeaderFields ?? [:] headers.updateValue(host, forKey: "host") @@ -252,4 +168,5 @@ extension AWSCognitoAuthPlugin { return requestBuilder } + } diff --git a/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift b/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift new file mode 100644 index 0000000000..5d598bd321 --- /dev/null +++ b/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift @@ -0,0 +1,44 @@ +// +// File.swift +// +// +// Created by Law, Michael on 2024-07-05. +// + +import Foundation +@_spi(InternalAmplifyConfiguration) import Amplify + +public struct AWSAppSyncConfiguration { + public let region: String + public let endpoint: URL + public let apiKey: String? + + public init(with amplifyOutputs: AmplifyOutputs) throws { + do { + let resolvedConfiguration = try amplifyOutputs.resolveConfiguration() + + guard let dataCategory = resolvedConfiguration.data else { + throw "" + } + + self.region = dataCategory.awsRegion + guard let endpoint = URL(string: dataCategory.url) else { + throw "" + } + self.endpoint = endpoint + self.apiKey = dataCategory.apiKey + + } catch { + throw error + } + } + + static var isRunningForSwiftUIPreviews: Bool { + ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil + } +} + + +extension AWSAppSyncConfiguration: DefaultLogger { } + +extension String: Error { } diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift index d5b53f6dd0..5ca39220ae 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift @@ -13,7 +13,7 @@ import Foundation public class AmplifyAWSCredentialsProvider: AWSClientRuntime.CredentialsProviding { public init() { - CommonRuntimeKit.initialize() + } public func getCredentials() async throws -> AWSClientRuntime.AWSCredentials { let authSession = try await Amplify.Auth.fetchAuthSession() diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift index b953a7d7a8..1640e3c27b 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift @@ -10,6 +10,7 @@ import Amplify import ClientRuntime import AWSClientRuntime import AwsCommonRuntimeKit +import AWSPluginsCore public protocol AWSSignatureV4Signer { func sigV4SignedRequest(requestBuilder: SdkHttpRequestBuilder,