watson-developer-cloud / swift-sdk

:iphone: The Watson Swift SDK enables developers to quickly add Watson Cognitive Computing services to their Swift applications.
https://watson-developer-cloud.github.io/swift-sdk/
Apache License 2.0
877 stars 222 forks source link

Add support for token authentication #327

Closed glennrfisher closed 5 years ago

glennrfisher commented 8 years ago

Problem

The iOS SDK currently supports only HTTP basic authentication, requiring clients to authenticate with their service credentials. When directly embedded into an application, these service credentials are exposed to hackers (through decompilation and memory attacks on jailbroken devices).

Solution

By adding support for tokens, we can make it easier for application developers to protect their service credentials.

  1. Add support for clients to either (a) set a service's token, or (b) define a function that obtains tokens.
  2. Add support for clients to automatically refresh the service's tokens when they fail (e.g. expires).
  3. Add support to authenticate with services using tokens instead of HTTP basic authentication.

Steps 1 and 2 are fairly straightforward (we can use the authentication classes that were previous supported prior to the RestKit transition).

Step 3 is the challenging task (and the reason why we temporarily dropped support for tokens when transitioning to the RestKit architecture). This will probably require us to write a custom Alamofire Manager class to authenticate using tokens. It should efficiently refresh tokens on expiration (pausing all requests with expired tokens, then re-running them when the expired token is refreshed).

For more information about step 3, see this Alamofire issue and this StackOverflow post.

Timeline

We expect to complete this task by the end of Q3 or beginning of Q4.

Temporary Hack

While we add support for token authentication, there is a "hack" that clients can use to protect their credentials.

Security problems arise whenever credentials are embedded in an application. To secure credentials, clients should avoid embedding them in their app. To do so, clients can use a server to dispense their service credentials (as they will need to do for tokens in the future).

To explain the hack, let's begin by considering how a client would use token authentication:

  1. Client provisions a custom Bluemix application to dispense tokens.
  2. App contacts Bluemix application to retrieve a token.
  3. The app uses the token to authenticate with a Watson service.
  4. The app refreshes the token as needed.

So while we work on this issue, clients can replace the token dispenser with a credentials dispenser:

  1. Client provisions a custom Bluemix application to dispense service credentials.
  2. App contacts Bluemix application to retrieve service credentials.
  3. App instantiates service class using the retrieved service credentials.
  4. The app uses basic authentication to authenticate with the Watson service.

By using a credentials dispenser, clients can avoid embedding their service credentials in their app.

glennrfisher commented 8 years ago

Requirements:

These requirements can be satisfied with the AuthenticationStrategy and other authentication classes that were used before transitioning to the RestKit architecture.

The challenge with this issue does not lie in specifying tokens or how to refresh tokens--that's easily satisfied by reintroducing the classes mentioned above. Instead, the challenge is making use of these tokens in the networking calls.

The iOS SDK currently uses Alamofire's authenticate(username:password:) function for HTTP basic auth. We will need to write networking code that not only supports tokens, but efficiently refreshes tokens on expiration. Doing so will probably require us to author a custom Alamofire Manager class. See this Alamofire issue and this StackOverflow post for more information.

glennrfisher commented 8 years ago

See the documentation on using tokens with Watson services.

glennrfisher commented 8 years ago

Since we have a well-defined RestRequest class, it might actually be straightforward to replace Alamofire with NSURLSession...

This will take some investigating to see if it's the right move. Consider the benefits we get from Alamofire (well tested, optimized, easy-to-use, and lots of functionality) with NSURLSession.

glennrfisher commented 8 years ago

WatsonGateway is a useful reference here. It's how we executed networking requests with tokens before migrating to our current RestKit architecture.

WatsonGateway was heavily influenced by this StackOverflow post. The comments include a lot of helpful discussion.

Here's a quick description of the WatsonGateway.request(...) function:

  1. Create a cachedRequest in case we need it (this is a closure that will retry the request).
  2. If the token is already being refreshed, then cache the request.
  3. If the token is nil, then cache the request and refresh the token. If the refresh is successful, then we retry all of the cached requests.
  4. If the token is not-nil and isn't being refreshed, then we use NSURLSession to execute the request. If we receive a 401 Unauthorized error, we will refresh the token and try again (up to some maximum number of retries).
mkistler commented 5 years ago

This has been addressed by the IAM support now available in the Swift SDK.