Set clientSecret for okta-sdk programatically

We are integrating Okta into our mobile applications. We are following resource owner password flow and okta sdk (GitHub - okta/okta-oidc-ios: Okta with AppAuth).

In the documentation (readme) it says that If using the Resource Owner Password Grant, you must specify the clientSecret in Okta.plist. But it also mention that IMPORTANT: It is strongly discouraged to store a clientSecret on a distributed app. Please refer to OAuth 2.0 for Native Apps for more information.

So how can we set/update the clientSecret from the code (programmatically)? I could not find any method to set clientSecret in the SDK.

There is no way to set the client secret programmatically. Your options for using the Okta AppAuth SDK are:

  • Use the Resource Owner Password grant and store the client secret in Okta.plist, or
  • Use the Authorization Code grant (.login().start(view: ...)

The latter is more secure and is recommended. However, if you need a fully-native login experience, the Resource Owner Password grant is the only way to go.

Thanks for your answer. We are following Resource Owner Password and using SDK (.login().start(view: …) to get the accesstoken and refresh token. So when the access token is expired how can we get a new access token? Is there any SDK methods for that purpose? I saw one method OktaAuth.refresh(), but I could not find any examples for that. Could you please help me on this?

OktaAuth.refresh() should do it. See this function in the example app: https://github.com/okta/okta-sdk-appauth-ios/blob/master/Example/Okta/ViewController.swift#L23

What happens when you call refresh()? Are you requesting the offline_access scope during the initial request?

I am getting following error when I call the OktaAuth.refresh(). But my login call is working fine and I am able to get accesstoken and refreshToken etc after login success. We are following Resource Owner Password and using Okta SDK.

2017-12-20 16:37:25.778316-0800 OktaSample[10452:275416] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil’
*** First throw call stack:
(
0 CoreFoundation 0x0000000112f2f1cb __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010f57df41 objc_exception_throw + 48
2 CoreFoundation 0x0000000112f6ee8c _CFThrowFormattedException + 194
3 CoreFoundation 0x0000000112e5e6f1 -[__NSArrayM insertObject:atIndex:] + 1233
4 AppAuth 0x000000010eee4d23 -[OIDURLQueryComponent addParameter:value:] + 259
5 AppAuth 0x000000010eee2759 -[OIDTokenRequest URLRequest] + 809
6 AppAuth 0x000000010eed1eac +[OIDAuthorizationService performTokenRequest:callback:] + 92
7 AppAuth 0x000000010eed8357 -[OIDAuthState performActionWithFreshTokens:additionalRefreshParameters:] + 1671
8 AppAuth 0x000000010eed7cb0 -[OIDAuthState performActionWithFreshTokens:] + 64
9 OktaAuth 0x000000010ef1a623 _T08OktaAuth7refreshyyF + 659
10 OktaSample 0x000000010ebd81fc _T010OktaSample14ViewControllerC9btnRefrshyypF + 252
11 OktaSample 0x000000010ebd8d88 _T010OktaSample14ViewControllerC9btnRefrshyypFTo + 72
12 UIKit 0x000000010ff9cec9 -[UIApplication sendAction:to:from:forEvent:] + 83
13 UIKit 0x000000011011a1f6 -[UIControl sendAction:to:forEvent:] + 67
14 UIKit 0x000000011011a513 -[UIControl _sendActionsForEvents:withEvent:] + 450
15 UIKit 0x0000000110119440 -[UIControl touchesEnded:withEvent:] + 618
16 UIKit 0x0000000110012b1b -[UIWindow _sendTouchesForEvent:] + 2807
17 UIKit 0x000000011001423e -[UIWindow sendEvent:] + 4124
18 UIKit 0x000000010ffb7d96 -[UIApplication sendEvent:] + 352
19 UIKit 0x00000001108f9fce __dispatchPreprocessedEventFromEventQueue + 2809
20 UIKit 0x00000001108fcc23 __handleEventQueueInternal + 5957
21 CoreFoundation 0x0000000112ed22b1 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 17
22 CoreFoundation 0x0000000112f71d31 __CFRunLoopDoSource0 + 81
23 CoreFoundation 0x0000000112eb6c19 __CFRunLoopDoSources0 + 185
24 CoreFoundation 0x0000000112eb61ff __CFRunLoopRun + 1279
25 CoreFoundation 0x0000000112eb5a89 CFRunLoopRunSpecific + 409
26 GraphicsServices 0x00000001192d19c6 GSEventRunModal + 62
27 UIKit 0x000000010ff9b23c UIApplicationMain + 159
28 OktaSample 0x000000010ebdb737 main + 55
29 libdyld.dylib 0x0000000117321d81 start + 1
30 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

In My Okta.plist file I have given that offline_access

<key>scopes</key>
<array>
	<string>offline_access</string>
	<string>openid</string>
	<string>profile</string>
</array>

Can you double-check that you are receiving a refresh token? Make sure that the application config allows a refresh token:

image

If the response coming back from the Resource Owner Password grant includes a refresh token, it sounds like a bug in the iOS SDK. I’d recommend filing an issue here: https://github.com/okta/okta-sdk-appauth-ios/issues

Ping @jmelberg

I am receiving refresh token in .login("user@example.com", password: “password”)
.start(withPListConfig: “Okta”, view:self) in for Resource Owner Password flow. But when I try to call the OktaAuth.refresh() method, I am getting the crash as I said above.

@nate.barbettini & @shihabkb: This is a bug with how the authentication state is stored in the Okta AppAuth wrapper.

There is a very good chance we will be removing support for the Resource Owner Password OAuth 2.0 flow in the future, so I am not sure when/if this will be fixed.

@shihabkb: Is there a reason you are using this flow as opposed to the recommended Authorization Code Flow + PKCE? The Resource Owner Password flow is for private clients that can protect a client_secret. Native applications are to be treated as public clients. This is a less secure method, as the client_secret can be reverse engineered from the distributed app. See OAuth 2.0 for Native Apps for more information.