Skip to content

Commit

Permalink
Allow Auth credentials to be fetched via callback(async), backwards c…
Browse files Browse the repository at this point in the history
…ompatible (#62)

* Fetch auth token via callback

FOr OIDC or Cognito authentication, auth tokens often expire and need to be re-generated. Using a callback follows the model also used by aws-mobile-appsync-sdk-js

* removed log statement

* Enabled async retrieval of auth tokens with backwards compatibility

* Enabled async retrieval of auth tokens with backwards compatibility

* removed extra blank lines

* fixed copyright

* prefer fatal error instead of silent failure where Async provider is used

* ensure message is sent even when async provider is not used

* removed extra blank lines

* async token callback now accepts an error
  • Loading branch information
JohnRbk authored and rohandubal committed Sep 8, 2018
1 parent c9ce191 commit 1e245b9
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 32 deletions.
4 changes: 4 additions & 0 deletions AWSAppSyncClient.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
17E009DA1FCAB234005031DB /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E009B81FCAB233005031DB /* Result.swift */; };
17E009DB1FCAB234005031DB /* NormalizedCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E009B91FCAB234005031DB /* NormalizedCache.swift */; };
17E009DC1FCAB234005031DB /* ApolloClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E009BA1FCAB234005031DB /* ApolloClient.swift */; };
741880B0213878B400523CA8 /* AuthProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741880AF213878B400523CA8 /* AuthProviderTests.swift */; };
7C8D9BD0CFD94CE97870947D /* Pods_AWSAppSyncTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB93C78C2EEB30A1C3C24E2E /* Pods_AWSAppSyncTests.framework */; };
DF9468DB20E1CA4A00E40482 /* AWSNetworkTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF9468DA20E1CA4A00E40482 /* AWSNetworkTransport.swift */; };
E4EA2880833EDE9A29FEBC15 /* Pods_AWSAppSync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 621AB86198B779E235D8B962 /* Pods_AWSAppSync.framework */; };
Expand Down Expand Up @@ -174,6 +175,7 @@
17E009BA1FCAB234005031DB /* ApolloClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ApolloClient.swift; path = Apollo/Sources/Apollo/ApolloClient.swift; sourceTree = "<group>"; };
621AB86198B779E235D8B962 /* Pods_AWSAppSync.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AWSAppSync.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6A5E9B8BFB35E672A4BA10CD /* Pods-AWSAppSyncTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSAppSyncTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AWSAppSyncTests/Pods-AWSAppSyncTests.release.xcconfig"; sourceTree = "<group>"; };
741880AF213878B400523CA8 /* AuthProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthProviderTests.swift; sourceTree = "<group>"; };
9A210F34AE9783A62B36F8F2 /* Pods-AWSAppSyncTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSAppSyncTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AWSAppSyncTests/Pods-AWSAppSyncTests.debug.xcconfig"; sourceTree = "<group>"; };
AD111EB21748A599F5796B74 /* Pods-AWSAppSync.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSAppSync.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AWSAppSync/Pods-AWSAppSync.debug.xcconfig"; sourceTree = "<group>"; };
C830ED8003E746C4C6799F8E /* Pods-AWSAppSync.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSAppSync.release.xcconfig"; path = "Pods/Target Support Files/Pods-AWSAppSync/Pods-AWSAppSync.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -250,6 +252,7 @@
174F80AD2109229C00775D0D /* AWSAppSyncTests */ = {
isa = PBXGroup;
children = (
741880AF213878B400523CA8 /* AuthProviderTests.swift */,
174F80AE2109229C00775D0D /* AWSAppSyncTests.swift */,
174F80B02109229C00775D0D /* Info.plist */,
174F80B8210924B200775D0D /* EventsAPI.swift */,
Expand Down Expand Up @@ -565,6 +568,7 @@
files = (
174F80AF2109229C00775D0D /* AWSAppSyncTests.swift in Sources */,
174F80B9210924B200775D0D /* EventsAPI.swift in Sources */,
741880B0213878B400523CA8 /* AuthProviderTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
11 changes: 11 additions & 0 deletions AWSAppSyncClient/AWSAppSyncAuthProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@

import Foundation

// For using OIDC based authorization, this protocol needs to be implemented and passed to configuration object.
// Use this for cases where the OIDC token needs to be fetched asynchronously and requires a callback
public protocol AWSOIDCAuthProviderAsync: AWSOIDCAuthProvider {
func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void)
}

// For AuthProviders that use a callback, the getLatestAuthToken is defaulted to return an empty string
extension AWSOIDCAuthProviderAsync {
public func getLatestAuthToken() -> String { fatalError("Callback method required") }
}

// For using OIDC based authorization, this protocol needs to be implemented and passed to configuration object.
public protocol AWSOIDCAuthProvider {
/// The method should fetch the token and return it to the client for using in header request.
Expand Down
97 changes: 65 additions & 32 deletions AWSAppSyncClient/AWSAppSyncHTTPNetworkTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,6 @@ public class AWSAppSyncHTTPNetworkTransport: AWSNetworkTransport {
request.setValue(NSDate().aws_stringValue(AWSDateISO8601DateFormat2), forHTTPHeaderField: "X-Amz-Date")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("aws-sdk-ios/2.6.18 AppSyncClient", forHTTPHeaderField: "User-Agent")
if self.authType == .apiKey {
request.setValue(self.apiKeyAuthProvider!.getAPIKey(), forHTTPHeaderField: "x-api-key")
} else if self.authType == .oidcToken {
request.setValue(self.oidcAuthProvider!.getLatestAuthToken(), forHTTPHeaderField: "authorization")
} else if self.authType == .amazonCognitoUserPools {
request.setValue(self.userPoolsAuthProvider!.getLatestAuthToken(), forHTTPHeaderField: "authorization")
}
}

/// Send a data payload to a server and return a response.
Expand Down Expand Up @@ -186,19 +179,73 @@ public class AWSAppSyncHTTPNetworkTransport: AWSNetworkTransport {

let mutableRequest = ((request as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!

if self.authType == .awsIAM {
sendRequestWithAuth(mutableRequest: mutableRequest, sendRequest: sendRequest, onError: { error in
completionHandler?(nil, error)
})
}

/// Updates the sendRequest with the appropriate authentication parameters
/// In the case of a token retrieval error, the errorCallback is invoked
private func sendRequestWithAuth(mutableRequest: NSMutableURLRequest, sendRequest: @escaping (URLRequest) -> Void, onError errorCallback: @escaping ((Error) -> Void)) -> Void {

switch self.authType {

case .awsIAM:
let signer:AWSSignatureV4Signer = AWSSignatureV4Signer(credentialsProvider: self.credentialsProvider, endpoint: self.endpoint)
signer.interceptRequest(mutableRequest).continueWith { _ in
return nil
}.continueWith { _ in
sendRequest(request: mutableRequest as URLRequest)
sendRequest(mutableRequest as URLRequest)
}
case .apiKey:
mutableRequest.setValue(self.apiKeyAuthProvider!.getAPIKey(), forHTTPHeaderField: "x-api-key")
sendRequest( mutableRequest as URLRequest)
case .oidcToken:
if let provider = self.oidcAuthProvider as? AWSOIDCAuthProviderAsync {

provider.getLatestAuthToken { (token, error) in
if let error = error {
errorCallback(error)
}
else if let token = token {
mutableRequest.setValue(token, forHTTPHeaderField: "authorization")
sendRequest( mutableRequest as URLRequest)
}
else {
fatalError("Invalid data returned in token callback")
}
}
} else if let provider = self.oidcAuthProvider {
mutableRequest.setValue(provider.getLatestAuthToken(), forHTTPHeaderField: "authorization")
sendRequest( mutableRequest as URLRequest)
} else {
fatalError("Authentication provider not set")
}
case .amazonCognitoUserPools:
if let provider = self.userPoolsAuthProvider as? AWSOIDCAuthProviderAsync {

provider.getLatestAuthToken { (token, error) in
if let error = error {
errorCallback(error)
}
else if let token = token {
mutableRequest.setValue(token, forHTTPHeaderField: "authorization")
sendRequest( mutableRequest as URLRequest)
}
else {
fatalError("Invalid data returned in token callback")
}
}
} else if let provider = self.userPoolsAuthProvider {
mutableRequest.setValue(provider.getLatestAuthToken(), forHTTPHeaderField: "authorization")
sendRequest( mutableRequest as URLRequest)
} else {
fatalError("Authentication provider not set")
}
} else {
sendRequest(request: mutableRequest as URLRequest)
}

}


/// Send a GraphQL operation to a server and return a response for a subscription.
///
/// - Parameters:
Expand Down Expand Up @@ -259,16 +306,9 @@ public class AWSAppSyncHTTPNetworkTransport: AWSNetworkTransport {

let mutableRequest = ((request as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!

if self.authType == .awsIAM {
let signer:AWSSignatureV4Signer = AWSSignatureV4Signer(credentialsProvider: self.credentialsProvider, endpoint: self.endpoint)
signer.interceptRequest(mutableRequest).continueWith { _ in
return nil
}.continueWith { _ in
sendRequest(request: mutableRequest as URLRequest)
}
} else {
sendRequest(request: mutableRequest as URLRequest)
}
sendRequestWithAuth(mutableRequest: mutableRequest, sendRequest: sendRequest, onError: { error in
completionHandler(nil, error)
})

return networkTransportOperation
}
Expand Down Expand Up @@ -334,16 +374,9 @@ public class AWSAppSyncHTTPNetworkTransport: AWSNetworkTransport {

let mutableRequest = ((request as NSURLRequest).mutableCopy() as? NSMutableURLRequest)!

if self.authType == .awsIAM {
let signer:AWSSignatureV4Signer = AWSSignatureV4Signer(credentialsProvider: self.credentialsProvider, endpoint: self.endpoint)
signer.interceptRequest(mutableRequest).continueWith { _ in
return nil
}.continueWith { _ in
sendRequest(request: mutableRequest as URLRequest)
}
} else {
sendRequest(request: mutableRequest as URLRequest)
}
sendRequestWithAuth(mutableRequest: mutableRequest, sendRequest: sendRequest, onError: { error in
completionHandler(nil, error)
})

return networkTransportOperation
}
Expand Down
Loading

0 comments on commit 1e245b9

Please sign in to comment.