Credential convenience class
Convenience class that provides methods and properties for using a user's authentication tokens.
Once a user or other identity is authenticated within an application, the tokens' lifecycle must be properly managed to ensure it is correctly refreshed as needed, is stored in a secure manner, and can be used to perform requests on behalf of the user. This class provides capabilities to accomplish all these tasks, while ensuring a convenient developer experience.
Overview
The primary goal of using authentication APIs is to ultimately receive credentials for your users, allowing your them to do something with your application. Credentials for authorizing requests to a developer's Resource Server may include:
- OAuth2 tokens
- API keys
- SAML assertions
- Other mechanisms authorizing requests
At this time the Okta Client SDK only supports OAuth2 tokens, but the naming and structure of the SDK is defined in such a way as to allow future expansion as needed.
If a developer doesn't have some way to save and retrieve those credentials for later use, users would need to sign into an application every time they opened it.
Since no two applications are alike, and usage patterns vary wildly, Okta's AuthFoundation library exposes several features that enable you to access and manage user credentials conveniently, while still affording you the ability to customize behavior to suit your needs.
A Token object is used to represent information related to a user's OAuth2 tokens, in particular the access_token, which is used to authorize requests made to an Authorization Server. This class supports serialization, allowing it to be easily persisted to keep users signed in. Using and storing these objects manually can be cumbersome to use on a day-to-day basis, and can result in tricky race-conditions when tokens are refreshed, so to simplify the developer workflow, this Credential class exists as a way to abstract interactions with a client's OAuth2 Tokens.
Instances of this class not only encapsulate the token object itself, but exposes convenience methods and properties to simplify common operations, such as refreshing access tokens, fetching the user's information / metadata, introspecting specific token strings, or revoking some or all of the credential's tokens.
To ensure these operations can function, an OAuth2Client object is automatically created on behalf of the developer, which is used to perform operations against the Authorization Server associated with the Token.
Capabilities
Developer customization
The token management classes and types are intentionally abstracted from the developer for the sake of conveniences, only being exposed through these static properties to support a developer's ability to override internal business logic.
In order to ensure proper thread-safety and a convenient and contextual point of entry for developers, the Credential class supports several static properties that represent the SDK's ability to define overridden extensions to this subsystem:
credentialDataSource
The credentialDataSource static property should be a thread-safe mutable getter/setter property that allows a developer to assign a CredentialDataSource object.
This property should act as a wrapper around the CredentialCoordinator property representing this value.
This value should be retained by the underlying implementation, and will serve as the basis for creating and caching in-memory representations of the Credential class for their corresponding Tokens.
tokenStorage
The tokenStorage static property should be a thread-safe mutable getter/setter property that allows a developer to assign a TokenStorage object.
This property should act as a wrapper around the CredentialCoordinator property representing this value.
This value should be retained by the underlying implementation, and will serve as the basis for interacting with the underlying token storage mechanism, which will perform CRUD operations for tokens.
Static functions & properties
store
Stores the supplied token into storage. This function accepts optional developer-supplied tags, and where applicable, security options to configure accessibility and biometrics settings.
- Swift
// Store using defaults
let credential = try Credential.store(token)
// Store with tags
let credential = try Credential.store(
token,
tags: ["displayName": "My User"])
// Store with security options
try Credential.store(
token,
tags: ["displayName": "My User"],
security: [
.accessibility(.afterFirstUnlock),
.accessControl(.biometryAny),
.accessGroup("com.example.myApp.shared")
])
Since there is no industry-defined value that defines a consistent unique identifier to represent a logical access identity, the SDK will generate a UUID or some other unique identifier that represents this token.
From this point forward, this locally-generated ID will be used to uniquely represent this token, or whichever value it is replaced with when the token is refreshed.
Once the token is stored, its unique ID should become available within the allIDs property.
allIDs
This returns an array of all locally-generated unique IDs for the tokens stored within token storage.
- Swift
for tokenId in Credential.allIDs {
// Do something with the ID
}
default
Mutable property that gets or sets the Credential object that is currently assigned as the default. This ultimately functions as a two-step process:
- Fetch the default token ID from token storage
- Get and return the Credential with the given ID.
As a developer convenience, the default credential should automatically be assigned when the first token is stored.
- Swift
if let credential = Credential.default {
// Show the UI with the given credential
showUser(credential)
} else {
showLoginScreen()
}
// Switch the default user
Credential.default = someNewUser
withId
Returns a Credential object represented by the given unique ID.
- Swift
func showUser(with tokenId: String) throws {
if let credential = try Credential.with(id: tokenId) {
// Do something with the credential
}
}
find
Finds the array of credentials that match the given metadata criteria, supplied through a lambda function. This function should be provided information that includes the credential's developer-assigned tags, as well as the claims available within the token's ID Token (if available).
- Swift
try Credential.store(token: newToken, tags: ["service": "purchase"])
// Later ...
let matchingCredentials = try Credential.find(where: { meta in
meta.tags["service"] == "purchase"
})
let admins = try Credential.find(where: { meta in
let roles = meta[.roles] as? [String]
return roles?.contains("admin")
})
refreshGraceInterval
Defines the application's global default time grace interval used when requesting a token to be refreshed. When the refreshIfNeeded function is called without an explicit grace interval, this static property's value is used as the default.
- Swift
// Uses the default grace interval
try await credential.refreshIfNeeded()
// Uses an explicit grace interval
try await credential.refreshIfNeeded(graceInterval: 10)
OAuth2 client conveniences
The OAuth2Client class provides functions and properties for interacting with an Authorization Server, which often includes operations that are performed on or with tokens. Therefore the Credential class provides convenience wrappers that simplify interactions with the OAuth2Client including, but not limited to:
- introspect tokens
- revoke tokens
- fetching user information
- refreshing (or refreshing if needed) access tokens
- supplying access to the appropriate OAuth2Client object for the token
refresh
Refreshes the token immediately, regardless of whether or not it's expired.
- Swift
do {
try await credential.refresh()
// Use the token ...
} catch {
// Intercept errors
}
refreshIfNeeded
Checks the token's expiration time and, after coordinating the local system time, only triggers a refresh if the token will expire within a certain grace interval.
do {
try await credential.refreshIfNeeded()
// Use the token ...
} catch {
// Intercept errors
}
revoke
Revokes one or more tokens for the credential, based on arguments supplied. Defaults to all tokens (e.g. access_token, refresh_token, device_secret, etc).
introspect
Introspects the given token type (e.g. access_token, etc) using an enum type to determine what kind of token to introspect.
userInfo
Fetches the user information for the credential's token.
Request authorization
Since the ultimate goal of working with authentication services is to authorize client API requests to its Resource Server, this convenience function asynchronously attaches the appropriate headers or other values to the supplied outgoing HTTP request.
This may involve multiple steps, based on the type of token, or the client configuration:
- Preemptively refreshing a token if it's expired, or is soon to expire
- Bearer token
Authorizationheader - DPoP token
Authorizationheader, accompanied by aDPoPproof header - Other mechanisms as appropriate in the future
Authorizes a network request object before it's sent to the HTTP client.
- Swift
let apiUrl = URL(string: "https://example.com/my/api")
var myRequest = URLRequest(url: apiUrl)
myRequest.httpMethod = "POST"
myRequest.httpBody = bodyData
// Refresh the token if needed, and authorize the
// request with Bearer or DPoP tokens.
try await credential.authorize(request: &myRequest)
// Send the request
let (data, response) = try await URLSession.shared.data(for: myRequest)