Skip to content

Commit c2bc304

Browse files
authored
Add OIDC nonce support (#4051)
1 parent 56b37d2 commit c2bc304

15 files changed

+294
-4
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "MainViewController.h"
18+
19+
#import "StaticContentTableViewManager.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
@interface MainViewController (Apple)
24+
25+
- (StaticContentTableViewSection *)appleAuthSection;
26+
27+
@end
28+
29+
NS_ASSUME_NONNULL_END
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "MainViewController+Apple.h"
18+
19+
#import <AuthenticationServices/AuthenticationServices.h>
20+
21+
#import "AppManager.h"
22+
#import "FirebaseAuth.h"
23+
#import "MainViewController+Internal.h"
24+
25+
NS_ASSUME_NONNULL_BEGIN
26+
27+
@interface MainViewController () <ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding> {
28+
29+
}
30+
31+
@end
32+
33+
@implementation MainViewController (Apple)
34+
35+
- (StaticContentTableViewSection *)appleAuthSection API_AVAILABLE(ios(13.0)) {
36+
__weak typeof(self) weakSelf = self;
37+
return [StaticContentTableViewSection sectionWithTitle:@"Apple Auth" cells:@[
38+
[StaticContentTableViewCell cellWithTitle:@"Sign in with Apple"
39+
action:^{ [weakSelf signInWithApple]; }],
40+
[StaticContentTableViewCell cellWithTitle:@"Link with Apple"
41+
action:^{ [weakSelf linkWithApple]; }],
42+
[StaticContentTableViewCell cellWithTitle:@"Unlink with Apple"
43+
action:^{ [weakSelf unlinkFromProvider:@"apple.com" completion:nil]; }],
44+
[StaticContentTableViewCell cellWithTitle:@"Reauthenticate with Apple"
45+
action:^{ [weakSelf reauthenticateWithApple]; }],
46+
]];
47+
}
48+
49+
- (void)signInWithApple API_AVAILABLE(ios(13.0)) {
50+
ASAuthorizationAppleIDProvider* provider = [[ASAuthorizationAppleIDProvider alloc] init];
51+
ASAuthorizationAppleIDRequest* request = [provider createRequest];
52+
request.requestedScopes = @[ASAuthorizationScopeEmail, ASAuthorizationScopeFullName];
53+
request.nonce = @"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
54+
request.state = @"signIn";
55+
56+
ASAuthorizationController* controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
57+
controller.delegate = self;
58+
controller.presentationContextProvider = self;
59+
[controller performRequests];
60+
}
61+
62+
- (void)linkWithApple API_AVAILABLE(ios(13.0)) {
63+
ASAuthorizationAppleIDProvider* provider = [[ASAuthorizationAppleIDProvider alloc] init];
64+
ASAuthorizationAppleIDRequest* request = [provider createRequest];
65+
request.requestedScopes = @[ASAuthorizationScopeEmail, ASAuthorizationScopeFullName];
66+
request.nonce = @"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
67+
request.state = @"link";
68+
69+
ASAuthorizationController* controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
70+
controller.delegate = self;
71+
controller.presentationContextProvider = self;
72+
[controller performRequests];
73+
}
74+
75+
- (void)reauthenticateWithApple API_AVAILABLE(ios(13.0)) {
76+
ASAuthorizationAppleIDProvider* provider = [[ASAuthorizationAppleIDProvider alloc] init];
77+
ASAuthorizationAppleIDRequest* request = [provider createRequest];
78+
request.requestedScopes = @[ASAuthorizationScopeEmail, ASAuthorizationScopeFullName];
79+
request.nonce = @"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
80+
request.state = @"reauth";
81+
82+
ASAuthorizationController* controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
83+
controller.delegate = self;
84+
controller.presentationContextProvider = self;
85+
[controller performRequests];
86+
}
87+
88+
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
89+
ASAuthorizationAppleIDCredential* appleIDCredential = authorization.credential;
90+
NSString *idToken = [NSString stringWithUTF8String:[appleIDCredential.identityToken bytes]];
91+
FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com"
92+
IDToken:idToken
93+
rawNonce:@"foobar"
94+
accessToken:nil];
95+
96+
if ([appleIDCredential.state isEqualToString:@"signIn"]) {
97+
[FIRAuth.auth signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
98+
if (!error) {
99+
NSLog(@"%@", authResult.description);
100+
} else {
101+
NSLog(@"%@", error.description);
102+
}
103+
}];
104+
} else if ([appleIDCredential.state isEqualToString:@"link"]) {
105+
[FIRAuth.auth.currentUser linkWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
106+
if (!error) {
107+
NSLog(@"%@", authResult.description);
108+
} else {
109+
NSLog(@"%@", error.description);
110+
}
111+
}];
112+
} else if ([appleIDCredential.state isEqualToString:@"reauth"]) {
113+
[FIRAuth.auth.currentUser reauthenticateWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
114+
if (!error) {
115+
NSLog(@"%@", authResult.description);
116+
} else {
117+
NSLog(@"%@", error.description);
118+
}
119+
}];
120+
}
121+
}
122+
123+
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
124+
NSLog(@"%@", error.description);
125+
}
126+
127+
@end
128+
129+
NS_ASSUME_NONNULL_END

Example/Auth/Sample/MainViewController.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#import "MainViewController.h"
1818
#import "MainViewController+App.h"
19+
#import "MainViewController+Apple.h"
1920
#import "MainViewController+Auth.h"
2021
#import "MainViewController+AutoTests.h"
2122
#import "MainViewController+Custom.h"
@@ -244,6 +245,8 @@ - (void)updateTable {
244245
[weakSelf googleAuthSection],
245246
// Facebook Auth
246247
[weakSelf facebookAuthSection],
248+
// Apple Auth
249+
[weakSelf appleAuthSection],
247250
// OAuth
248251
[weakSelf oAuthSection],
249252
// Custom Auth

Example/Firebase.xcodeproj/project.pbxproj

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
40CC5510221B9B4700032423 /* CustomAuthTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 40CC5508221B9B4700032423 /* CustomAuthTests.m */; };
118118
40CC5512221B9B4700032423 /* AnonymousAuthTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 40CC550A221B9B4700032423 /* AnonymousAuthTests.m */; };
119119
40CC5513221B9B4700032423 /* FacebookAuthTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 40CC550B221B9B4700032423 /* FacebookAuthTests.m */; };
120+
40DED7BE2350F60300768D88 /* MainViewController+Apple.m in Sources */ = {isa = PBXBuildFile; fileRef = 40DED7BD2350F60200768D88 /* MainViewController+Apple.m */; };
120121
40EE3B822256B23200BE59CE /* MainViewController+GameCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EE3B812256B23200BE59CE /* MainViewController+GameCenter.m */; };
121122
40EE3B862256B5A000BE59CE /* MainViewController+Phone.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EE3B852256B5A000BE59CE /* MainViewController+Phone.m */; };
122123
40EE3B892256B9A600BE59CE /* MainViewController+User.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EE3B882256B9A600BE59CE /* MainViewController+User.m */; };
@@ -1082,6 +1083,8 @@
10821083
40CC550A221B9B4700032423 /* AnonymousAuthTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnonymousAuthTests.m; sourceTree = "<group>"; };
10831084
40CC550B221B9B4700032423 /* FacebookAuthTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FacebookAuthTests.m; sourceTree = "<group>"; };
10841085
40CC550C221B9B4700032423 /* FIRAuthApiTestsBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRAuthApiTestsBase.h; sourceTree = "<group>"; };
1086+
40DED7BC2350F60200768D88 /* MainViewController+Apple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MainViewController+Apple.h"; sourceTree = "<group>"; };
1087+
40DED7BD2350F60200768D88 /* MainViewController+Apple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MainViewController+Apple.m"; sourceTree = "<group>"; };
10851088
40EE3B802256B23200BE59CE /* MainViewController+GameCenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MainViewController+GameCenter.h"; sourceTree = "<group>"; };
10861089
40EE3B812256B23200BE59CE /* MainViewController+GameCenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "MainViewController+GameCenter.m"; sourceTree = "<group>"; };
10871090
40EE3B832256B39800BE59CE /* MainViewController+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MainViewController+Internal.h"; sourceTree = "<group>"; };
@@ -2303,6 +2306,8 @@
23032306
DE26D1E81F70333E004AE1D3 /* MainViewController.xib */,
23042307
40EE3B8D2256C0AD00BE59CE /* MainViewController+App.h */,
23052308
40EE3B8E2256C0AD00BE59CE /* MainViewController+App.m */,
2309+
40DED7BC2350F60200768D88 /* MainViewController+Apple.h */,
2310+
40DED7BD2350F60200768D88 /* MainViewController+Apple.m */,
23062311
40EE3BA22256C8DD00BE59CE /* MainViewController+Auth.h */,
23072312
40EE3BA32256C8DE00BE59CE /* MainViewController+Auth.m */,
23082313
40EE3B902256C2E800BE59CE /* MainViewController+AutoTests.h */,
@@ -4814,6 +4819,7 @@
48144819
DE26D24E1F7039BC004AE1D3 /* UIViewController+Alerts.m in Sources */,
48154820
DE26D24B1F7039BC004AE1D3 /* MainViewController.m in Sources */,
48164821
40EE3BA02256C77200BE59CE /* MainViewController+Custom.m in Sources */,
4822+
40DED7BE2350F60300768D88 /* MainViewController+Apple.m in Sources */,
48174823
40EE3B8C2256BBB200BE59CE /* MainViewController+OOB.m in Sources */,
48184824
DE26D24A1F7039BC004AE1D3 /* main.m in Sources */,
48194825
DE26D2481F7039BC004AE1D3 /* FacebookAuthProvider.m in Sources */,
@@ -6853,7 +6859,7 @@
68536859
DEBUG_INFORMATION_FORMAT = dwarf;
68546860
DEVELOPMENT_TEAM = 4ANB9W7R3P;
68556861
GCC_C_LANGUAGE_STANDARD = gnu11;
6856-
INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist";
6862+
INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist;
68576863
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
68586864
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
68596865
MTL_ENABLE_DEBUG_INFO = YES;
@@ -6887,7 +6893,7 @@
68876893
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
68886894
DEVELOPMENT_TEAM = 4ANB9W7R3P;
68896895
GCC_C_LANGUAGE_STANDARD = gnu11;
6890-
INFOPLIST_FILE = "$SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist";
6896+
INFOPLIST_FILE = $SRCROOT/DynamicLinks/FDLBuilderTestAppObjC/Info.plist;
68916897
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
68926898
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
68936899
MTL_ENABLE_DEBUG_INFO = NO;

Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
@interface FIROAuthCredential ()
2828

29+
@property(nonatomic, nullable) NSString *rawNonce;
30+
2931
- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE;
3032

3133
@end
@@ -40,12 +42,14 @@ - (nullable instancetype)initWithProvider:(NSString *)provider {
4042

4143
- (instancetype)initWithProviderID:(NSString *)providerID
4244
IDToken:(nullable NSString *)IDToken
45+
rawNonce:(nullable NSString *)rawNonce
4346
accessToken:(nullable NSString *)accessToken
4447
secret:(nullable NSString *)secret
4548
pendingToken:(nullable NSString *)pendingToken {
4649
self = [super initWithProvider:providerID];
4750
if (self) {
4851
_IDToken = IDToken;
52+
_rawNonce = rawNonce;
4953
_accessToken = accessToken;
5054
_pendingToken = pendingToken;
5155
_secret = secret;
@@ -56,8 +60,12 @@ - (instancetype)initWithProviderID:(NSString *)providerID
5660
- (instancetype)initWithProviderID:(NSString *)providerID
5761
sessionID:(NSString *)sessionID
5862
OAuthResponseURLString:(NSString *)OAuthResponseURLString {
59-
self =
60-
[self initWithProviderID:providerID IDToken:nil accessToken:nil secret:nil pendingToken:nil];
63+
self = [self initWithProviderID:providerID
64+
IDToken:nil
65+
rawNonce:nil
66+
accessToken:nil
67+
secret:nil
68+
pendingToken:nil];
6169
if (self) {
6270
_OAuthResponseURLString = OAuthResponseURLString;
6371
_sessionID = sessionID;
@@ -71,6 +79,7 @@ - (nullable instancetype)initWithVerifyAssertionResponse:(FIRVerifyAssertionResp
7179
response.oauthSecretToken.length) {
7280
return [self initWithProviderID:response.providerID
7381
IDToken:response.oauthIDToken
82+
rawNonce:nil
7483
accessToken:response.oauthAccessToken
7584
secret:response.oauthSecretToken
7685
pendingToken:response.pendingToken];
@@ -80,6 +89,7 @@ - (nullable instancetype)initWithVerifyAssertionResponse:(FIRVerifyAssertionResp
8089

8190
- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request {
8291
request.providerIDToken = _IDToken;
92+
request.providerRawNonce = _rawNonce;
8393
request.providerAccessToken = _accessToken;
8494
request.requestURI = _OAuthResponseURLString;
8595
request.sessionID = _sessionID;
@@ -95,11 +105,13 @@ + (BOOL)supportsSecureCoding {
95105

96106
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
97107
NSString *IDToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"IDToken"];
108+
NSString *rawNonce = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"rawNonce"];
98109
NSString *accessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"accessToken"];
99110
NSString *pendingToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"pendingToken"];
100111
NSString *secret = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"secret"];
101112
self = [self initWithProviderID:self.provider
102113
IDToken:IDToken
114+
rawNonce:rawNonce
103115
accessToken:accessToken
104116
secret:secret
105117
pendingToken:pendingToken];
@@ -108,6 +120,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
108120

109121
- (void)encodeWithCoder:(NSCoder *)aCoder {
110122
[aCoder encodeObject:self.IDToken forKey:@"IDToken"];
123+
[aCoder encodeObject:self.IDToken forKey:@"rawNonce"];
111124
[aCoder encodeObject:self.accessToken forKey:@"accessToken"];
112125
[aCoder encodeObject:self.pendingToken forKey:@"pendingToken"];
113126
[aCoder encodeObject:self.secret forKey:@"secret"];

Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ NS_ASSUME_NONNULL_BEGIN
4646
@brief Designated initializer.
4747
@param providerID The provider ID associated with the credential being created.
4848
@param IDToken The ID Token associated with the credential being created.
49+
@param rawNonce The raw nonce associated with the Auth credential being created.
4950
@param accessToken The access token associated with the credential being created.
5051
@param secret The secret associated with the credential being created.
5152
@param pendingToken The pending token associated with the credential being created.
5253
*/
5354
- (instancetype)initWithProviderID:(NSString *)providerID
5455
IDToken:(nullable NSString *)IDToken
56+
rawNonce:(nullable NSString *)rawNonce
5557
accessToken:(nullable NSString *)accessToken
5658
secret:(nullable NSString *)secret
5759
pendingToken:(nullable NSString *)pendingToken NS_DESIGNATED_INITIALIZER;

Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
7171
accessToken:(nullable NSString *)accessToken {
7272
return [[FIROAuthCredential alloc] initWithProviderID:providerID
7373
IDToken:IDToken
74+
rawNonce:nil
7475
accessToken:accessToken
7576
secret:nil
7677
pendingToken:nil];
@@ -80,11 +81,35 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
8081
accessToken:(NSString *)accessToken {
8182
return [[FIROAuthCredential alloc] initWithProviderID:providerID
8283
IDToken:nil
84+
rawNonce:nil
8385
accessToken:accessToken
8486
secret:nil
8587
pendingToken:nil];
8688
}
8789

90+
+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
91+
IDToken:(NSString *)IDToken
92+
rawNonce:(nullable NSString *)rawNonce
93+
accessToken:(nullable NSString *)accessToken {
94+
return [[FIROAuthCredential alloc] initWithProviderID:providerID
95+
IDToken:IDToken
96+
rawNonce:rawNonce
97+
accessToken:accessToken
98+
secret:nil
99+
pendingToken:nil];
100+
}
101+
102+
+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
103+
IDToken:(NSString *)IDToken
104+
rawNonce:(nullable NSString *)rawNonce {
105+
return [[FIROAuthCredential alloc] initWithProviderID:providerID
106+
IDToken:IDToken
107+
rawNonce:rawNonce
108+
accessToken:nil
109+
secret:nil
110+
pendingToken:nil];
111+
}
112+
88113
+ (instancetype)providerWithProviderID:(NSString *)providerID {
89114
return [[self alloc]initWithProviderID:providerID auth:[FIRAuth auth]];
90115
}

Firebase/Auth/Source/Backend/FIRAuthBackend.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@
342342
*/
343343
static NSString *const kSessionExpiredErrorMessage = @"SESSION_EXPIRED";
344344

345+
/** @var kMissingOrInvalidNonceErrorMessage
346+
@brief This is the error message the server will respond with if the nonce is missing or invalid.
347+
*/
348+
static NSString *const kMissingOrInvalidNonceErrorMessage = @"MISSING_OR_INVALID_NONCE";
349+
345350
/** @var kMissingAppTokenErrorMessage
346351
@brief This is the error message the server will respond with if the APNS token is missing in a
347352
verifyClient request.
@@ -1176,6 +1181,10 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM
11761181
return [FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:serverErrorMessage];
11771182
}
11781183

1184+
if ([shortErrorMessage isEqualToString:kMissingOrInvalidNonceErrorMessage]) {
1185+
return [FIRAuthErrorUtils missingOrInvalidNonceErrorWithMessage:serverDetailErrorMessage];
1186+
}
1187+
11791188
// In this case we handle an error that might be specified in the underlying errors dictionary,
11801189
// the error message in determined based on the @c reason key in the dictionary.
11811190
if (errorDictionary[kErrorsKey]) {

0 commit comments

Comments
 (0)