Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement headers support in getCredentials and awaitCredentials #699

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,37 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
minTtl: Int,
parameters: Map<String, String>,
forceRefresh: Boolean
): Credentials {
return awaitCredentials(scope, minTtl, parameters, mapOf(), forceRefresh)
}

/**
* Retrieves the credentials from the storage and refresh them if they have already expired.
* It will throw [CredentialsManagerException] if the saved access_token or id_token is null,
* or if the tokens have already expired and the refresh_token is null.
* This is a Coroutine that is exposed only for Kotlin.
*
* @param scope the scope to request for the access token. If null is passed, the previous scope will be kept.
* @param minTtl the minimum time in seconds that the access token should last before expiration.
* @param parameters additional parameters to send in the request to refresh expired credentials.
* @param headers additional headers to send in the request to refresh expired credentials.
* @param forceRefresh this will avoid returning the existing credentials and retrieves a new one even if valid credentials exist.
*/
@JvmSynthetic
@Throws(CredentialsManagerException::class)
public suspend fun awaitCredentials(
scope: String?,
minTtl: Int,
parameters: Map<String, String>,
headers: Map<String, String>,
forceRefresh: Boolean
): Credentials {
return suspendCancellableCoroutine { continuation ->
getCredentials(
scope,
minTtl,
parameters,
headers,
forceRefresh,
object : Callback<Credentials, CredentialsManagerException> {
override fun onSuccess(result: Credentials) {
Expand Down Expand Up @@ -201,6 +226,29 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
parameters: Map<String, String>,
forceRefresh: Boolean,
callback: Callback<Credentials, CredentialsManagerException>
) {
getCredentials(scope, minTtl, parameters, mapOf(), forceRefresh, callback)
}

/**
* Retrieves the credentials from the storage and refresh them if they have already expired.
* It will fail with [CredentialsManagerException] if the saved access_token or id_token is null,
* or if the tokens have already expired and the refresh_token is null.
*
* @param scope the scope to request for the access token. If null is passed, the previous scope will be kept.
* @param minTtl the minimum time in seconds that the access token should last before expiration.
* @param parameters additional parameters to send in the request to refresh expired credentials.
* @param headers additional headers to send in the request to refresh expired credentials.
* @param forceRefresh this will avoid returning the existing credentials and retrieves a new one even if valid credentials exist.
* @param callback the callback that will receive a valid [Credentials] or the [CredentialsManagerException].
*/
public fun getCredentials(
scope: String?,
minTtl: Int,
parameters: Map<String, String>,
headers: Map<String, String>,
forceRefresh: Boolean,
callback: Callback<Credentials, CredentialsManagerException>
) {
serialExecutor.execute {
val accessToken = storage.retrieveString(KEY_ACCESS_TOKEN)
Expand Down Expand Up @@ -240,6 +288,10 @@ public class CredentialsManager @VisibleForTesting(otherwise = VisibleForTesting
request.addParameter("scope", scope)
}

for (header in headers) {
request.addHeader(header.key, header.value)
}

try {
val fresh = request.execute()
val expiresAt = fresh.expiresAt.time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
return false
}
if (resultCode == Activity.RESULT_OK) {
continueGetCredentials(scope, minTtl, emptyMap(), forceRefresh, decryptCallback!!)
continueGetCredentials(scope, minTtl, emptyMap(), emptyMap(), forceRefresh, decryptCallback!!)
} else {
decryptCallback!!.onFailure(CredentialsManagerException("The user didn't pass the authentication challenge."))
decryptCallback = null
Expand Down Expand Up @@ -281,12 +281,41 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
minTtl: Int,
parameters: Map<String, String>,
forceRefresh: Boolean,
): Credentials {
return awaitCredentials(scope, minTtl, parameters, mapOf(), forceRefresh)
}

/**
* Tries to obtain the credentials from the Storage. The method will return [Credentials].
* If something unexpected happens, then [CredentialsManagerException] exception will be thrown. Some devices are not compatible
* at all with the cryptographic implementation and will have [CredentialsManagerException.isDeviceIncompatible] return true.
* This is a Coroutine that is exposed only for Kotlin.
*
* If a LockScreen is setup and [SecureCredentialsManager.requireAuthentication] was called, the user will be asked to authenticate before accessing
* the credentials. Your activity must override the [Activity.onActivityResult] method and call
* [SecureCredentialsManager.checkAuthenticationResult] with the received values.
*
* @param scope the scope to request for the access token. If null is passed, the previous scope will be kept.
* @param minTtl the minimum time in seconds that the access token should last before expiration.
* @param parameters additional parameters to send in the request to refresh expired credentials.
* @param headers additional headers to send in the request to refresh expired credentials.
* @param forceRefresh this will avoid returning the existing credentials and retrieves a new one even if valid credentials exist.
*/
@JvmSynthetic
@Throws(CredentialsManagerException::class)
public suspend fun awaitCredentials(
scope: String?,
minTtl: Int,
parameters: Map<String, String>,
headers: Map<String, String>,
forceRefresh: Boolean,
): Credentials {
return suspendCancellableCoroutine { continuation ->
getCredentials(
scope,
minTtl,
parameters,
headers,
forceRefresh,
object : Callback<Credentials, CredentialsManagerException> {
override fun onSuccess(result: Credentials) {
Expand Down Expand Up @@ -384,6 +413,34 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
parameters: Map<String, String>,
forceRefresh: Boolean,
callback: Callback<Credentials, CredentialsManagerException>
) {
getCredentials(scope, minTtl, parameters, mapOf(), forceRefresh, callback)
}

/**
* Tries to obtain the credentials from the Storage. The callback's [Callback.onSuccess] method will be called with the result.
* If something unexpected happens, the [Callback.onFailure] method will be called with the error. Some devices are not compatible
* at all with the cryptographic implementation and will have [CredentialsManagerException.isDeviceIncompatible] return true.
*
*
* If a LockScreen is setup and [SecureCredentialsManager.requireAuthentication] was called, the user will be asked to authenticate before accessing
* the credentials. Your activity must override the [Activity.onActivityResult] method and call
* [SecureCredentialsManager.checkAuthenticationResult] with the received values.
*
* @param scope the scope to request for the access token. If null is passed, the previous scope will be kept.
* @param minTtl the minimum time in seconds that the access token should last before expiration.
* @param parameters additional parameters to send in the request to refresh expired credentials.
* @param headers additional headers to send in the request to refresh expired credentials.
* @param forceRefresh this will avoid returning the existing credentials and retrieves a new one even if valid credentials exist.
* @param callback the callback to receive the result in.
*/
public fun getCredentials(
scope: String?,
minTtl: Int,
parameters: Map<String, String>,
headers: Map<String, String>,
forceRefresh: Boolean,
callback: Callback<Credentials, CredentialsManagerException>
) {
if (!hasValidCredentials(minTtl.toLong())) {
callback.onFailure(CredentialsManagerException("No Credentials were previously set."))
Expand All @@ -402,7 +459,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
?: activity?.startActivityForResult(authIntent, authenticationRequestCode)
return
}
continueGetCredentials(scope, minTtl, parameters, forceRefresh, callback)
continueGetCredentials(scope, minTtl, parameters, headers, forceRefresh, callback)
}

/**
Expand Down Expand Up @@ -451,6 +508,7 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
scope: String?,
minTtl: Int,
parameters: Map<String, String>,
headers: Map<String, String>,
forceRefresh: Boolean,
callback: Callback<Credentials, CredentialsManagerException>
) {
Expand Down Expand Up @@ -532,6 +590,10 @@ public class SecureCredentialsManager @VisibleForTesting(otherwise = VisibleForT
request.addParameter("scope", scope)
}

for (header in headers) {
request.addHeader(header.key, header.value)
}

val freshCredentials: Credentials
try {
val fresh = request.execute()
Expand Down