Skip to content

Commit e88b52f

Browse files
authored
Add OIDC nonce support (#4071)
1 parent 351a917 commit e88b52f

File tree

11 files changed

+125
-2
lines changed

11 files changed

+125
-2
lines changed

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]) {

Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ NS_ASSUME_NONNULL_BEGIN
6666
*/
6767
@property(nonatomic, copy, nullable) NSString *providerIDToken;
6868

69+
/** @property providerRawNonce
70+
@brief An raw nonce from the IDP.
71+
*/
72+
@property(nonatomic, copy, nullable) NSString *providerRawNonce;
73+
6974
/** @property returnIDPCredential
7075
@brief Whether the response should return the IDP credential directly.
7176
*/

Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
*/
3434
static NSString *const kProviderIDTokenKey = @"id_token";
3535

36+
/** @var kProviderNonceKey
37+
@brief The key for the "nonce" value in the request.
38+
*/
39+
static NSString *const kProviderNonceKey = @"nonce";
40+
3641
/** @var kProviderAccessTokenKey
3742
@brief The key for the "access_token" value in the request.
3843
*/
@@ -115,6 +120,11 @@ - (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)
115120
value:_providerIDToken]];
116121
}
117122

123+
if (_providerRawNonce) {
124+
[queryItems addObject:[NSURLQueryItem queryItemWithName:kProviderNonceKey
125+
value:_providerRawNonce]];
126+
}
127+
118128
if (_providerAccessToken) {
119129
[queryItems addObject:[NSURLQueryItem queryItemWithName:kProviderAccessTokenKey
120130
value:_providerAccessToken]];

Firebase/Auth/Source/Public/FIRAuthErrors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ typedef NS_ENUM(NSInteger, FIRAuthErrorCode) {
343343
*/
344344
FIRAuthErrorCodeGameKitNotLinked = 17076,
345345

346+
/** Indicates that the nonce is missing or invalid.
347+
*/
348+
FIRAuthErrorCodeMissingOrInvalidNonce = 17094,
349+
346350
/** Indicates an error for when the client identifier is missing.
347351
*/
348352
FIRAuthErrorCodeMissingClientIdentifier = 17993,

Firebase/Auth/Source/Public/FIROAuthProvider.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,35 @@ NS_SWIFT_NAME(OAuthProvider)
8585
+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
8686
accessToken:(NSString *)accessToken;
8787

88+
/** @fn credentialWithProviderID:IDToken:rawNonce:accessToken:
89+
@brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID, ID
90+
token, raw nonce and access token.
91+
92+
@param providerID The provider ID associated with the Auth credential being created.
93+
@param IDToken The IDToken associated with the Auth credential being created.
94+
@param rawNonce The raw nonce associated with the Auth credential being created.
95+
@param accessToken The accessstoken associated with the Auth credential be created, if
96+
available.
97+
@return A FIRAuthCredential for the specified provider ID, ID token and access token.
98+
*/
99+
+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
100+
IDToken:(NSString *)IDToken
101+
rawNonce:(nullable NSString *)rawNonce
102+
accessToken:(nullable NSString *)accessToken;
103+
104+
/** @fn credentialWithProviderID:IDToken:rawNonce:
105+
@brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID using
106+
an ID token and raw nonce.
107+
108+
@param providerID The provider ID associated with the Auth credential being created.
109+
@param IDToken The IDToken associated with the Auth credential being created.
110+
@param rawNonce The raw nonce associated with the Auth credential being created.
111+
@return A FIRAuthCredential.
112+
*/
113+
+ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
114+
IDToken:(NSString *)IDToken
115+
rawNonce:(nullable NSString *)rawNonce;
116+
88117
/** @fn init
89118
@brief This class is not meant to be initialized.
90119
*/

Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,13 @@ NS_ASSUME_NONNULL_BEGIN
556556
*/
557557
+ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status;
558558

559+
/** @fn missingOrInvalidNonceErrorWithMessage:
560+
@brief Constructs an @c NSError with the code and message provided.
561+
@param message Error message from the backend, if any.
562+
@return The nullable NSError instance associated with the given error message, if one is found.
563+
*/
564+
+ (NSError *)missingOrInvalidNonceErrorWithMessage:(nullable NSString *)message;
565+
559566
@end
560567

561568
NS_ASSUME_NONNULL_END

Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@
463463
static NSString *const kFIRAuthErrorMessageRejectedCredential =
464464
@"The request contains malformed or mismatching credentials.";
465465

466+
/** @var kFIRAuthErrorMessageMissingOrInvalidNonce
467+
@brief Error message constant describing @c FIRAuthErrorCodeMissingOrInvalidNonce errors.
468+
*/
469+
static NSString *const kFIRAuthErrorMessageMissingOrInvalidNonce =
470+
@"The request contains malformed or mismatched credentials.";
471+
466472
/** @var FIRAuthErrorDescription
467473
@brief The error descrioption, based on the error code.
468474
@remarks No default case so that we get a compiler warning if a new value was added to the enum.
@@ -599,6 +605,8 @@
599605
return kFIRAuthErrorMessageDynamicLinkNotActivated;
600606
case FIRAuthErrorCodeRejectedCredential:
601607
return kFIRAuthErrorMessageRejectedCredential;
608+
case FIRAuthErrorCodeMissingOrInvalidNonce:
609+
return kFIRAuthErrorMessageMissingOrInvalidNonce;
602610
}
603611
}
604612

@@ -738,6 +746,8 @@
738746
return @"ERROR_DYNAMIC_LINK_NOT_ACTIVATED";
739747
case FIRAuthErrorCodeRejectedCredential:
740748
return @"ERROR_REJECTED_CREDENTIAL";
749+
case FIRAuthErrorCodeMissingOrInvalidNonce:
750+
return @"ERROR_MISSING_OR_INVALID_NONCE";
741751
}
742752
}
743753

@@ -1179,6 +1189,10 @@ + (NSError *)invalidDynamicLinkDomainErrorWithMessage:(nullable NSString *)messa
11791189
return [self errorWithCode:FIRAuthInternalErrorCodeInvalidDynamicLinkDomain message:message];
11801190
}
11811191

1192+
+ (NSError *)missingOrInvalidNonceErrorWithMessage:(nullable NSString *)message {
1193+
return [self errorWithCode:FIRAuthInternalErrorCodeMissingOrInvalidNonce message:message];
1194+
}
1195+
11821196
+ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status {
11831197
NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status];
11841198
return [self errorWithCode:FIRAuthInternalErrorCodeKeychainError userInfo:@{

0 commit comments

Comments
 (0)