aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Zsika Phillip <protocol86@users.noreply.github.com>2018-03-29 18:16:45 -0700
committerGravatar GitHub <noreply@github.com>2018-03-29 18:16:45 -0700
commitea490a2c6492e41e892397e044477f778ce358b8 (patch)
tree2d94cb515dc84d77da8574e03e2165b0aa8ebab1
parent744f9daf43c06d920966773c5d6607377b6a6230 (diff)
Custom claims client api (#1004)
* Adds custom claims API to client * Ammends branch Adds: - Deprecation messages - Fixes auth result keys - Ammends sample app - Adds unit tests * fixes typo switches “to” to “so”
-rw-r--r--Example/Auth/Sample/MainViewController.m52
-rw-r--r--Example/Auth/Tests/FIRUserTests.m136
-rw-r--r--Firebase/Auth/Source/FIRAuthTokenResult.m110
-rw-r--r--Firebase/Auth/Source/FIRAuthTokenResult_Internal.h37
-rw-r--r--Firebase/Auth/Source/FIRUser.m73
-rw-r--r--Firebase/Auth/Source/Public/FIRAuthTokenResult.h66
-rw-r--r--Firebase/Auth/Source/Public/FIRUser.h49
-rw-r--r--Firebase/Auth/Source/Public/FirebaseAuth.h1
8 files changed, 518 insertions, 6 deletions
diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m
index f6893d1..18f1e02 100644
--- a/Example/Auth/Sample/MainViewController.m
+++ b/Example/Auth/Sample/MainViewController.m
@@ -28,6 +28,7 @@
#import "FIROAuthProvider.h"
#import "FIRPhoneAuthCredential.h"
#import "FIRPhoneAuthProvider.h"
+#import "FIRAuthTokenResult.h"
#import "FirebaseAuth.h"
#import "CustomTokenDataEntryViewController.h"
#import "FacebookAuthProvider.h"
@@ -55,6 +56,16 @@ typedef void (^testAutomationCallback)(NSError *_Nullable error);
*/
static NSString *const kTokenGetButtonText = @"Get Token";
+/** @var kGetTokenResultButtonText
+ @brief The text of the "Get Token Result" button.
+ */
+static NSString *const kGetTokenResultButtonText = @"Get Token Result";
+
+/** @var kGetTokenResultForceButtonText
+ @brief The text of the "Force Token Result" button.
+ */
+static NSString *const kGetTokenResultForceButtonText = @"Force Token Result";
+
/** @var kTokenRefreshButtonText
@brief The text of the "Refresh Token" button.
*/
@@ -826,7 +837,11 @@ typedef enum {
[StaticContentTableViewCell cellWithTitle:kTokenGetButtonText
action:^{ [weakSelf getUserTokenWithForce:NO]; }],
[StaticContentTableViewCell cellWithTitle:kTokenRefreshButtonText
- action:^{ [weakSelf getUserTokenWithForce:YES]; }]
+ action:^{ [weakSelf getUserTokenWithForce:YES]; }],
+ [StaticContentTableViewCell cellWithTitle:kGetTokenResultButtonText
+ action:^{ [weakSelf getUserTokenResultWithForce:NO]; }],
+ [StaticContentTableViewCell cellWithTitle:kGetTokenResultForceButtonText
+ action:^{ [weakSelf getUserTokenResultWithForce:YES]; }],
]],
[StaticContentTableViewSection sectionWithTitle:kSectionTitleLinkUnlinkAccounts cells:@[
[StaticContentTableViewCell cellWithTitle:kLinkWithGoogleText
@@ -2101,13 +2116,46 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
}
/** @fn getUserTokenWithForce:
- @brief Gets the token from @c FIRUser , optionally a refreshed one.
+ @brief Gets the token from @c FIRUser, optionally a refreshed one.
@param force Whether the refresh is forced or not.
*/
- (void)getUserTokenWithForce:(BOOL)force {
[[self user] getIDTokenForcingRefresh:force completion:[self tokenCallback]];
}
+/** @fn getUserTokenResultWithForce:
+ @brief Gets the token result object from @c FIRUser, optionally a refreshed one.
+ @param force Whether the refresh is forced or not.
+ */
+- (void)getUserTokenResultWithForce:(BOOL)force {
+
+ [[self user] getIDTokenResultForcingRefresh:force
+ completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ if (error) {
+ [self showMessagePromptWithTitle:kTokenRefreshErrorAlertTitle
+ message:error.localizedDescription
+ showCancelButton:NO
+ completion:nil];
+ [self logFailure:@"refresh token failed" error:error];
+ return;
+ }
+ [self logSuccess:@"refresh token succeeded."];
+ NSMutableString *message =
+ [[NSMutableString alloc] initWithString:
+ [NSString stringWithFormat:@"Token : %@\n", tokenResult.token]];
+ [message appendString:[NSString stringWithFormat:@"Auth Date : %@\n", tokenResult.authDate]];
+ [message appendString:
+ [NSString stringWithFormat:@"EXP Date : %@\n", tokenResult.expirationDate]];
+ [message appendString:
+ [NSString stringWithFormat:@"Issued Date : %@\n", tokenResult.issuedAtDate]];
+ [self showMessagePromptWithTitle:kTokenRefreshedAlertTitle
+ message:message
+ showCancelButton:NO
+ completion:nil];
+ }];
+}
+
/** @fn getAppTokenWithForce:
@brief Gets the token from @c FIRApp , optionally a refreshed one.
@param force Whether the refresh is forced or not.
diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m
index 68ee265..be269ee 100644
--- a/Example/Auth/Tests/FIRUserTests.m
+++ b/Example/Auth/Tests/FIRUserTests.m
@@ -29,6 +29,10 @@
#import "FIRAuthBackend.h"
#import "FIRAuthGlobalWorkQueue.h"
#import "FIRAuthOperationType.h"
+#import "FIRAuthTokenResult.h"
+#import "FIRSecureTokenService.h"
+#import "FIRSecureTokenRequest.h"
+#import "FIRSecureTokenResponse.h"
#import "FIRGetAccountInfoRequest.h"
#import "FIRGetAccountInfoResponse.h"
#import "FIRSetAccountInfoRequest.h"
@@ -61,7 +65,16 @@ static NSString *const kAPIKey = @"FAKE_API_KEY";
/** @var kAccessToken
@brief The fake access token.
*/
-static NSString *const kAccessToken = @"ACCESS_TOKEN";
+static NSString *const kAccessToken = @"eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZDBhODBiYzh"
+ "jM2U5NDM0OTc0ZDFmMWRiNTEifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItc2EtdXBncm"
+ "FkZWQiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIs"
+ "InN1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1MjIzNjQxNTcsImVtYWlsIjoiYXVuaXRlc3R1c2"
+ "VyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6"
+ "WyJhdW5pdGVzdHVzZXJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0=.WFQqSrpVnxx7m"
+ "UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_BKkcbZuq8O7iDVpOvqKC"
+ "3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN"
+ "hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW"
+ "lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA";
/** @var kNewAccessToken
@brief A new value for the fake access token.
@@ -233,6 +246,18 @@ static NSTimeInterval const kLastSignInDateTimeIntervalInSeconds = 1505858583;
*/
static const NSTimeInterval kExpectationTimeout = 2;
+/** @extention FIRSecureTokenService
+ @brief Extends the FIRSecureTokenService class to expose one private method for testing only.
+ */
+@interface FIRSecureTokenService ()
+
+/** @fn hasValidAccessToken
+ @brief private method exposed so it can be mocked to prevent the fake expiration date from
+ affecting the the unit tests.
+ */
+- (BOOL)hasValidAccessToken;
+@end
+
/** @class FIRUserTests
@brief Tests for @c FIRUser .
*/
@@ -899,6 +924,115 @@ static const NSTimeInterval kExpectationTimeout = 2;
OCMVerifyAll(_mockBackend);
}
+/** @fn testGetIDTokenResultSuccess
+ @brief Tests the flow of a successful @c getIDTokenResultWithCompletion: call.
+ */
+- (void)testGetIDTokenResultSuccess {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ id mockSecureTokenService = OCMClassMock([FIRSecureTokenService class]);
+ OCMStub([mockSecureTokenService hasValidAccessToken]).andReturn(YES);
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [user getIDTokenResultWithCompletion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(tokenResult.token, kAccessToken);
+ XCTAssertTrue(tokenResult.issuedAtDate &&
+ [tokenResult.issuedAtDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.authDate && [tokenResult.authDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.expirationDate &&
+ [tokenResult.expirationDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.claims && [tokenResult.claims isKindOfClass:[NSDictionary class]]);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testGetIDTokenResultForcingRefreshSuccess
+ @brief Tests the flow of a successful @c getIDTokenResultForcingRefresh:completion: call.
+ */
+- (void)testGetIDTokenResultForcingRefreshSuccess {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend secureToken:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRSecureTokenRequest *_Nullable request,
+ FIRSecureTokenResponseCallback callback) {
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ id mockSecureTokenResponse = OCMClassMock([FIRSecureTokenResponse class]);
+ OCMStub([mockSecureTokenResponse accessToken]).andReturn(kAccessToken);
+ callback(mockSecureTokenResponse, nil);
+ });
+ });
+ [user getIDTokenResultForcingRefresh:YES
+ completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(tokenResult.token, kAccessToken);
+ XCTAssertTrue(tokenResult.issuedAtDate &&
+ [tokenResult.issuedAtDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.authDate && [tokenResult.authDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.expirationDate &&
+ [tokenResult.expirationDate isKindOfClass:[NSDate class]]);
+ XCTAssertTrue(tokenResult.claims && [tokenResult.claims isKindOfClass:[NSDictionary class]]);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testGetIDTokenResultForcingRefreshFailure
+ @brief Tests the flow of a failed @c getIDTokenResultForcingRefresh:completion: call.
+ */
+- (void)testGetIDTokenResultForcingRefreshFailure {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend secureToken:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRSecureTokenRequest *_Nullable request,
+ FIRSecureTokenResponseCallback callback) {
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+
+ callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:[NSError new]]);
+ });
+ });
+ [user getIDTokenResultForcingRefresh:YES
+ completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(tokenResult);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeNetworkError);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testReloadFailure
@brief Tests the flow of a failed @c reloadWithCompletion: call.
*/
diff --git a/Firebase/Auth/Source/FIRAuthTokenResult.m b/Firebase/Auth/Source/FIRAuthTokenResult.m
new file mode 100644
index 0000000..81e9920
--- /dev/null
+++ b/Firebase/Auth/Source/FIRAuthTokenResult.m
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FIRAuthTokenResult_internal.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @var kExpirationDateKey
+ @brief The key used to encode the expirationDate property for NSSecureCoding.
+ */
+static NSString *const kExpirationDateKey = @"expiratinDate";
+
+/** @var kTokenKey
+ @brief The key used to encode the token property for NSSecureCoding.
+ */
+static NSString *const kTokenKey = @"token";
+
+/** @var kAuthDateKey
+ @brief The key used to encode the authDate property for NSSecureCoding.
+ */
+static NSString *const kAuthDateKey = @"authDate";
+
+/** @var kIssuedDateKey
+ @brief The key used to encode the issuedDate property for NSSecureCoding.
+ */
+static NSString *const kIssuedDateKey = @"issuedDate";
+
+/** @var kSignInProviderKey
+ @brief The key used to encode the signInProvider property for NSSecureCoding.
+ */
+static NSString *const kSignInProviderKey = @"signInProvider";
+
+/** @var kClaimsKey
+ @brief The key used to encode the claims property for NSSecureCoding.
+ */
+static NSString *const kClaimsKey = @"claims";
+
+@implementation FIRAuthTokenResult
+
+- (instancetype)initWithToken:(NSString *)token
+ expirationDate:(NSDate *)expirationDate
+ authDate:(NSDate *)authDate
+ issuedAtDate:(NSDate *)issuedAtDate
+ signInProvider:(NSString *)signInProvider
+ claims:(NSDictionary *)claims {
+ self = [super init];
+ if (self) {
+ _token = token;
+ _expirationDate = expirationDate;
+ _authDate = authDate;
+ _issuedAtDate = issuedAtDate;
+ _signInProvider = signInProvider;
+ _claims = claims;
+ }
+ return self;
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
+ NSString *token =
+ [aDecoder decodeObjectOfClass:[NSDate class] forKey:kTokenKey];
+ NSDate *expirationDate =
+ [aDecoder decodeObjectOfClass:[NSDate class] forKey:kExpirationDateKey];
+ NSDate *authDate =
+ [aDecoder decodeObjectOfClass:[NSDate class] forKey:kAuthDateKey];
+ NSDate *issuedAtDate =
+ [aDecoder decodeObjectOfClass:[NSDate class] forKey:kAuthDateKey];
+ NSString *signInProvider =
+ [aDecoder decodeObjectOfClass:[NSString class] forKey:kSignInProviderKey];
+ NSDictionary<NSString *, NSString *> *claims =
+ [aDecoder decodeObjectOfClass:[NSDictionary<NSString *, NSString *> class] forKey:kClaimsKey];
+
+ return [self initWithToken:token
+ expirationDate:expirationDate
+ authDate:authDate
+ issuedAtDate:issuedAtDate
+ signInProvider:signInProvider
+ claims:claims];
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [aCoder encodeObject:_token forKey:kTokenKey];
+ [aCoder encodeObject:_expirationDate forKey:kExpirationDateKey];
+ [aCoder encodeObject:_authDate forKey:kAuthDateKey];
+ [aCoder encodeObject:_issuedAtDate forKey:kIssuedDateKey];
+ [aCoder encodeObject:_signInProvider forKey:kSignInProviderKey];
+ [aCoder encodeObject:_claims forKey:kClaimsKey];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/FIRAuthTokenResult_Internal.h b/Firebase/Auth/Source/FIRAuthTokenResult_Internal.h
new file mode 100644
index 0000000..2914f2a
--- /dev/null
+++ b/Firebase/Auth/Source/FIRAuthTokenResult_Internal.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #import <Foundation/Foundation.h>
+
+ #import "FIRAuthTokenResult.h"
+
+ NS_ASSUME_NONNULL_BEGIN
+
+/** @extension FIRAuthAPNSTokenResult
+ @brief An internal class used to expose internal methods of FIRAuthAPNSTokenResult.
+ */
+@interface FIRAuthTokenResult () <NSSecureCoding>
+
+- (instancetype)initWithToken:(NSString *)token
+ expirationDate:(NSDate *)expirationDate
+ authDate:(NSDate *)authDate
+ issuedAtDate:(NSDate *)issuedAtDate
+ signInProvider:(NSString *)signInProvider
+ claims:(NSDictionary *)claims;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m
index a77db75..fc7930f 100644
--- a/Firebase/Auth/Source/FIRUser.m
+++ b/Firebase/Auth/Source/FIRUser.m
@@ -29,6 +29,7 @@
#import "FIRAuth_Internal.h"
#import "FIRAuthBackend.h"
#import "FIRAuthRequestConfiguration.h"
+#import "FIRAuthTokenResult_Internal.h"
#import "FIRDeleteAccountRequest.h"
#import "FIRDeleteAccountResponse.h"
#import "FIREmailAuthProvider.h"
@@ -784,18 +785,88 @@ static void callInMainThreadWithAuthDataResultAndError(
- (void)getIDTokenForcingRefresh:(BOOL)forceRefresh
completion:(nullable FIRAuthTokenCallback)completion {
+ [self getIDTokenResultForcingRefresh:forceRefresh
+ completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(tokenResult.token, error);
+ });
+ }
+ }];
+}
+
+- (void)getIDTokenResultWithCompletion:(nullable FIRAuthTokenResultCallback)completion {
+ [self getIDTokenResultForcingRefresh:NO
+ completion:^(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(tokenResult, error);
+ });
+ }
+ }];
+}
+
+- (void)getIDTokenResultForcingRefresh:(BOOL)forceRefresh
+ completion:(nullable FIRAuthTokenResultCallback)completion {
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
[self internalGetTokenForcingRefresh:forceRefresh
callback:^(NSString *_Nullable token, NSError *_Nullable error) {
+ FIRAuthTokenResult *tokenResult;
+ if (token) {
+ tokenResult = [self parseIDToken:token error:&error];
+ }
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
- completion(token, error);
+ completion(tokenResult, error);
});
}
}];
});
}
+- (FIRAuthTokenResult *)parseIDToken:(NSString *)token error:(NSError **)error {
+ error = nil;
+ NSArray *tokenStringArray = [token componentsSeparatedByString:@"."];
+ // The token payload is always the second index of the array.
+ NSMutableString *tokenPayload = [[NSMutableString alloc] initWithString:tokenStringArray[1]];
+
+ // Pad the token payload with "=" signs if the payload's length is not a multple of 4.
+ int remainder = tokenPayload.length % 4 != 0;
+ if (remainder) {
+ while (remainder --) {
+ [tokenPayload appendString:@"="];
+ }
+ }
+ NSData *decodedTokenPayloadData =
+ [[NSData alloc] initWithBase64EncodedString:tokenPayload
+ options:NSDataBase64DecodingIgnoreUnknownCharacters];
+ NSDictionary *tokenPayloadDictionary =
+ [NSJSONSerialization JSONObjectWithData:decodedTokenPayloadData
+ options:kNilOptions
+ error:error];
+ if (error) {
+ return nil;
+ }
+
+ NSDate *expDate =
+ [NSDate dateWithTimeIntervalSinceNow:[tokenPayloadDictionary[@"exp"] doubleValue]];
+ NSDate *authDate =
+ [NSDate dateWithTimeIntervalSinceNow:[tokenPayloadDictionary[@"auth_time"] doubleValue]];
+ NSDate *issuedDate =
+ [NSDate dateWithTimeIntervalSinceNow:[tokenPayloadDictionary[@"iat"] doubleValue]];
+ FIRAuthTokenResult *result =
+ [[FIRAuthTokenResult alloc] initWithToken:token
+ expirationDate:expDate
+ authDate:authDate
+ issuedAtDate:issuedDate
+ signInProvider:tokenPayloadDictionary[@"sign_in_provider"]
+ claims:tokenPayloadDictionary];
+ return result;
+}
+
- (void)getTokenForcingRefresh:(BOOL)forceRefresh
completion:(nullable FIRAuthTokenCallback)completion {
[self getIDTokenForcingRefresh:forceRefresh completion:completion];
diff --git a/Firebase/Auth/Source/Public/FIRAuthTokenResult.h b/Firebase/Auth/Source/Public/FIRAuthTokenResult.h
new file mode 100644
index 0000000..11487dc
--- /dev/null
+++ b/Firebase/Auth/Source/Public/FIRAuthTokenResult.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIRAuthTokenResult
+ @brief A data class containing the ID token JWT string and other properties associated with the
+ token including the decoded payload claims if available.
+ */
+NS_SWIFT_NAME(AuthTokenResult)
+@interface FIRAuthTokenResult : NSObject
+
+/** @property token
+ @brief Stores the JWT string of the ID token.
+ */
+@property (nonatomic, readonly) NSString *token;
+
+/** @property expirationDate
+ @brief Stores the ID token's expiration date.
+ */
+@property (nonatomic, readonly) NSDate *expirationDate;
+
+/** @property authDate
+ @brief Stores the ID token's authentication date.
+ @remarks This is the date the user was signed in and NOT the date the token was refreshed.
+ */
+@property (nonatomic, readonly) NSDate *authDate;
+
+/** @property issuedAtDate
+ @brief Stores the date that the ID token was issued.
+ @remarks This is the date last refreshed and NOT the last authentication date.
+ */
+@property (nonatomic, readonly) NSDate *issuedAtDate;
+
+/** @property signInProvider
+ @brief Stores sign-in provider through which the token was obtained.
+ @remarks This does not necesssarily map to provider IDs.
+ */
+@property (nonatomic, readonly) NSString *signInProvider;
+
+/** @property claims
+ @brief Stores the entire payload of claims found on the ID token. This includes the standard
+ reserved claims as well as custom claims set by the developer via the Admin SDK.
+ */
+@property (nonatomic, readonly) NSDictionary<NSString *, NSString *> *claims;
+
+
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/Public/FIRUser.h b/Firebase/Auth/Source/Public/FIRUser.h
index 0cf872f..7198eac 100644
--- a/Firebase/Auth/Source/Public/FIRUser.h
+++ b/Firebase/Auth/Source/Public/FIRUser.h
@@ -20,6 +20,7 @@
#import "FIRAuthDataResult.h"
#import "FIRUserInfo.h"
+@class FIRAuthTokenResult;
@class FIRPhoneAuthCredential;
@class FIRUserProfileChangeRequest;
@class FIRUserMetadata;
@@ -39,6 +40,21 @@ NS_ASSUME_NONNULL_BEGIN
typedef void (^FIRAuthTokenCallback)(NSString *_Nullable token, NSError *_Nullable error)
NS_SWIFT_NAME(AuthTokenCallback);
+/** @typedef FIRAuthTokenResultCallback
+ @brief The type of block called when a token is ready for use.
+ @see FIRUser.getIDTokenResultWithCompletion:
+ @see FIRUser.getIDTokenResultForcingRefresh:withCompletion:
+
+ @param tokenResult Optionally; an object containing the raw access token string as well as other
+ useful data pertaining to the token.
+ @param error Optionally; the error which occurred - or nil if the request was successful.
+
+ @remarks One of: `token` or `error` will always be non-nil.
+ */
+typedef void (^FIRAuthTokenResultCallback)(FIRAuthTokenResult *_Nullable tokenResult,
+ NSError *_Nullable error)
+ NS_SWIFT_NAME(AuthTokenResultCallback);
+
/** @typedef FIRUserProfileChangeCallback
@brief The type of block called when a user profile change has finished.
@@ -250,6 +266,34 @@ NS_SWIFT_NAME(User)
- (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential
completion:(nullable FIRAuthDataResultCallback) completion;
+/** @fn getIDTokenResultWithCompletion:
+ @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
+
+ @param completion Optionally; the block invoked when the token is available. Invoked
+ asynchronously on the main thread in the future.
+
+ @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
+ */
+- (void)getIDTokenResultWithCompletion:(nullable FIRAuthTokenResultCallback)completion
+ NS_SWIFT_NAME(getIDTokenResult(completion:));
+
+/** @fn getIDTokenResultForcingRefresh:completion:
+ @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
+
+ @param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason
+ other than an expiration.
+ @param completion Optionally; the block invoked when the token is available. Invoked
+ asynchronously on the main thread in the future.
+
+ @remarks The authentication token will be refreshed (by making a network request) if it has
+ expired, or if `forceRefresh` is YES.
+
+ @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
+ */
+- (void)getIDTokenResultForcingRefresh:(BOOL)forceRefresh
+ completion:(nullable FIRAuthTokenResultCallback)completion
+ NS_SWIFT_NAME(getIDTokenResult(forcingRefresh:completion:));
+
/** @fn getIDTokenWithCompletion:
@brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
@@ -259,7 +303,7 @@ NS_SWIFT_NAME(User)
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
*/
- (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion
- NS_SWIFT_NAME(getIDToken(completion:));
+ NS_SWIFT_NAME(getIDToken(completion:)) __attribute__((deprecated));
/** @fn getTokenWithCompletion:
@brief Please use `getIDTokenWithCompletion:` instead.
@@ -286,7 +330,8 @@ NS_SWIFT_NAME(User)
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
*/
- (void)getIDTokenForcingRefresh:(BOOL)forceRefresh
- completion:(nullable FIRAuthTokenCallback)completion;
+ completion:(nullable FIRAuthTokenCallback)completion
+ __attribute__((deprecated));
/** @fn getTokenForcingRefresh:completion:
@brief Please use getIDTokenForcingRefresh:completion instead.
diff --git a/Firebase/Auth/Source/Public/FirebaseAuth.h b/Firebase/Auth/Source/Public/FirebaseAuth.h
index 409ac73..cd7e4a4 100644
--- a/Firebase/Auth/Source/Public/FirebaseAuth.h
+++ b/Firebase/Auth/Source/Public/FirebaseAuth.h
@@ -22,6 +22,7 @@
#import "FIRAuthCredential.h"
#import "FIRAuthDataResult.h"
#import "FIRAuthErrors.h"
+#import "FIRAuthTokenResult.h"
#import "FirebaseAuthVersion.h"
#import "FIREmailAuthProvider.h"
#import "FIRFacebookAuthProvider.h"