Skip to content

Commit 04ab6b2

Browse files
fix(iOS): omit state when additionalParameters.state is null
1 parent 6f9090c commit 04ab6b2

4 files changed

Lines changed: 19 additions & 1 deletion

File tree

.changeset/tidy-pianos-shave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-app-auth': patch
3+
---
4+
5+
Fix iOS sending `NSNull` as the OAuth `state` when `additionalParameters.state` is null.

docs/docs/usage/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ See specific example [configurations for your provider](/docs/category/providers
4444
- **register** - (`{ [key: string]: value }`) headers to be passed during registration request.
4545
- **additionalHeaders** - (`{ [key: string]: value }`) _IOS_ you can specify additional headers to be passed for all authorize, refresh, and register requests.
4646
- **useNonce** - (`boolean`) (default: true) optionally allows not sending the nonce parameter, to support non-compliant providers. To specify custom nonce, provide it in `additionalParameters` under the `nonce` key.
47+
- **state** - to specify a custom state, provide it in `additionalParameters` under the `state` key. A random state is generated when the key is absent. Pass `null` to omit the parameter entirely, to support providers that do not echo it back. Only omit it if you understand the CSRF tradeoff, and note that a provider returning a state you did not send will still fail validation.
4748
- **usePKCE** - (`boolean`) (default: true) optionally allows not sending the code_challenge parameter and skipping PKCE code verification, to support non-compliant providers.
4849
- **skipCodeExchange** - (`boolean`) (default: false) just return the authorization response, instead of automatically exchanging the authorization code. This is useful if this exchange needs to be done manually (not client-side)
4950
- **iosCustomBrowser** - (`string`) (default: undefined) _IOS_ override the used browser for authorization, used to open an external browser. If no value is provided, the `ASWebAuthenticationSession` or `SFSafariViewController` are used by the `AppAuth-iOS` library.

packages/react-native-app-auth/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ interface BuiltInParameters {
5959
display?: 'page' | 'popup' | 'touch' | 'wap';
6060
login_prompt?: string;
6161
prompt?: 'consent' | 'login' | 'none' | 'select_account';
62+
/** Custom state. Pass null to omit the parameter, for providers that do not echo it back. */
63+
state?: string | null;
6264
}
6365

6466
export type BaseAuthConfiguration = BaseConfiguration & {

packages/react-native-app-auth/ios/RNAppAuth.m

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,16 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
344344
[mutableDict removeObjectForKey:@"state"];
345345
[mutableDict removeObjectForKey:@"nonce"];
346346

347+
// A null from JS bridges to NSNull, which is truthy in ObjC, so a plain
348+
// ternary would send NSNull as the state. Absent generates, null omits.
349+
id requestedState = additionalParameters[@"state"];
350+
NSString *state = nil;
351+
if (requestedState == nil) {
352+
state = [[self class] generateState];
353+
} else if (![requestedState isKindOfClass:[NSNull class]]) {
354+
state = requestedState;
355+
}
356+
347357
// builds authentication request
348358
OIDAuthorizationRequest *request =
349359
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
@@ -353,7 +363,7 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
353363
scope:[OIDScopeUtilities scopesWithArray:scopes]
354364
redirectURL:[NSURL URLWithString:redirectUrl]
355365
responseType:OIDResponseTypeCode
356-
state: additionalParameters[@"state"] ? additionalParameters[@"state"] : [[self class] generateState]
366+
state:state
357367
nonce:nonce
358368
codeVerifier:codeVerifier
359369
codeChallenge:codeChallenge

0 commit comments

Comments
 (0)