Merge pull request #2896 from jhugman/jhugman/2334-add-ttl-to-get-access-token
2334 - Add ttl to get_access_token.
This commit is contained in:
Коммит
f39ad2f44f
|
@ -3,3 +3,7 @@
|
|||
# Unreleased Changes
|
||||
|
||||
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.57.0...master)
|
||||
|
||||
## FxA Client
|
||||
|
||||
- Added an optional `ttl` parameter to `getAccessToken`, to limit the cache length of the token.
|
||||
|
|
|
@ -241,14 +241,15 @@ class FirefoxAccount(handle: FxaHandle, persistCallback: PersistCallback?) : Aut
|
|||
* It may modify the persisted account state.
|
||||
*
|
||||
* @param scope Single OAuth scope (no spaces) for which the client wants access
|
||||
* @param ttl time in seconds for which the token will be valid
|
||||
* @return [AccessTokenInfo] that stores the token, along with its scopes and keys when complete
|
||||
* @throws FxaException.Unauthorized We couldn't provide an access token
|
||||
* for this scope. The caller should then start the OAuth Flow again with
|
||||
* the desired scope.
|
||||
*/
|
||||
fun getAccessToken(scope: String): AccessTokenInfo {
|
||||
fun getAccessToken(scope: String, ttl: Long? = null): AccessTokenInfo {
|
||||
val buffer = rustCallWithLock { e ->
|
||||
LibFxAFFI.INSTANCE.fxa_get_access_token(this.handle.get(), scope, e)
|
||||
LibFxAFFI.INSTANCE.fxa_get_access_token(this.handle.get(), scope, ttl ?: 0L, e)
|
||||
}
|
||||
this.tryPersistState()
|
||||
try {
|
||||
|
|
|
@ -51,7 +51,7 @@ internal interface LibFxAFFI : Library {
|
|||
fun fxa_get_manage_devices_url(fxa: FxaHandle, entrypoint: String, e: RustError.ByReference): Pointer?
|
||||
|
||||
fun fxa_complete_oauth_flow(fxa: FxaHandle, code: String, state: String, e: RustError.ByReference)
|
||||
fun fxa_get_access_token(fxa: FxaHandle, scope: String, e: RustError.ByReference): RustBuffer.ByValue
|
||||
fun fxa_get_access_token(fxa: FxaHandle, scope: String, ttl: Long, e: RustError.ByReference): RustBuffer.ByValue
|
||||
fun fxa_get_session_token(fxa: FxaHandle, e: RustError.ByReference): Pointer?
|
||||
fun fxa_get_current_device_id(fxa: FxaHandle, e: RustError.ByReference): Pointer?
|
||||
fun fxa_authorize_auth_code(fxa: FxaHandle, clientId: String, scope: String, state: String, accessType: String, e: RustError.ByReference): Pointer?
|
||||
|
|
|
@ -21,6 +21,6 @@ fn main() {
|
|||
let code = &query_params["code"];
|
||||
let state = &query_params["state"];
|
||||
fxa.complete_oauth_flow(&code, &state).unwrap();
|
||||
let oauth_info = fxa.get_access_token(SCOPES[0]);
|
||||
let oauth_info = fxa.get_access_token(SCOPES[0], None);
|
||||
println!("access_token: {:?}", oauth_info);
|
||||
}
|
||||
|
|
|
@ -315,12 +315,14 @@ pub extern "C" fn fxa_retry_migrate_from_session_token(
|
|||
pub extern "C" fn fxa_get_access_token(
|
||||
handle: u64,
|
||||
scope: FfiStr<'_>,
|
||||
ttl: u64,
|
||||
error: &mut ExternError,
|
||||
) -> ByteBuffer {
|
||||
log::debug!("fxa_get_access_token");
|
||||
ACCOUNTS.call_with_result_mut(error, handle, |fxa| {
|
||||
let scope = scope.as_str();
|
||||
fxa.get_access_token(scope)
|
||||
let time_left = if ttl > 0 { Some(ttl) } else { None };
|
||||
fxa.get_access_token(scope, time_left)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -58,13 +58,18 @@ open class FirefoxAccount: RustFxAccount {
|
|||
|
||||
/// Try to get an OAuth access token.
|
||||
///
|
||||
/// `ttl` corresponds to the time in seconds for which the token will be used.
|
||||
/// Throws `FirefoxAccountError.Unauthorized` if we couldn't provide an access token
|
||||
/// for this scope. The caller should then start the OAuth Flow again with
|
||||
/// the desired scope.
|
||||
open func getAccessToken(scope: String, completionHandler: @escaping (AccessTokenInfo?, Error?) -> Void) {
|
||||
open func getAccessToken(
|
||||
scope: String,
|
||||
ttl: UInt64? = nil,
|
||||
completionHandler: @escaping (AccessTokenInfo?, Error?) -> Void
|
||||
) {
|
||||
DispatchQueue.global().async {
|
||||
do {
|
||||
let tokenInfo = try super.getAccessToken(scope: scope)
|
||||
let tokenInfo = try super.getAccessToken(scope: scope, ttl: ttl)
|
||||
DispatchQueue.main.async { completionHandler(tokenInfo, nil) }
|
||||
} catch {
|
||||
DispatchQueue.main.async { completionHandler(nil, error) }
|
||||
|
|
|
@ -48,10 +48,10 @@ class FxAccount: RustFxAccount {
|
|||
}
|
||||
}
|
||||
|
||||
override func getAccessToken(scope: String) throws -> AccessTokenInfo {
|
||||
override func getAccessToken(scope: String, ttl: UInt64? = nil) throws -> AccessTokenInfo {
|
||||
defer { tryPersistState() }
|
||||
return try notifyAuthErrors {
|
||||
try super.getAccessToken(scope: scope)
|
||||
try super.getAccessToken(scope: scope, ttl: ttl)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -190,10 +190,14 @@ open class FxAccountManager {
|
|||
}
|
||||
|
||||
/// Try to get an OAuth access token.
|
||||
public func getAccessToken(scope: String, completionHandler: @escaping (Result<AccessTokenInfo, Error>) -> Void) {
|
||||
public func getAccessToken(
|
||||
scope: String,
|
||||
ttl: UInt64? = nil,
|
||||
completionHandler: @escaping (Result<AccessTokenInfo, Error>) -> Void
|
||||
) {
|
||||
DispatchQueue.global().async {
|
||||
do {
|
||||
let tokenInfo = try self.requireAccount().getAccessToken(scope: scope)
|
||||
let tokenInfo = try self.requireAccount().getAccessToken(scope: scope, ttl: ttl)
|
||||
DispatchQueue.main.async { completionHandler(.success(tokenInfo)) }
|
||||
} catch {
|
||||
DispatchQueue.main.async { completionHandler(.failure(error)) }
|
||||
|
|
|
@ -59,6 +59,7 @@ void fxa_complete_oauth_flow(FirefoxAccountHandle handle,
|
|||
|
||||
FxARustBuffer fxa_get_access_token(FirefoxAccountHandle handle,
|
||||
const char *_Nonnull scope,
|
||||
uint64_t ttl,
|
||||
FxAError *_Nonnull out);
|
||||
|
||||
char *_Nullable fxa_get_session_token(FirefoxAccountHandle handle,
|
||||
|
|
|
@ -131,12 +131,13 @@ open class RustFxAccount {
|
|||
|
||||
/// Try to get an OAuth access token.
|
||||
///
|
||||
/// `ttl` corresponds to the time in seconds for which the token will be used.
|
||||
/// Throws `FirefoxAccountError.Unauthorized` if we couldn't provide an access token
|
||||
/// for this scope. The caller should then start the OAuth Flow again with
|
||||
/// the desired scope.
|
||||
open func getAccessToken(scope: String) throws -> AccessTokenInfo {
|
||||
open func getAccessToken(scope: String, ttl: UInt64? = nil) throws -> AccessTokenInfo {
|
||||
let ptr = try rustCall { err in
|
||||
fxa_get_access_token(self.raw, scope, err)
|
||||
fxa_get_access_token(self.raw, scope, ttl ?? .zero, err)
|
||||
}
|
||||
defer { fxa_bytebuffer_free(ptr) }
|
||||
let msg = try! MsgTypes_AccessTokenInfo(serializedData: Data(rustBuffer: ptr))
|
||||
|
|
|
@ -38,6 +38,7 @@ pub trait FxAClient {
|
|||
&self,
|
||||
config: &Config,
|
||||
refresh_token: &str,
|
||||
ttl: Option<u64>,
|
||||
scopes: &[&str],
|
||||
) -> Result<OAuthTokenResponse>;
|
||||
fn access_token_with_session_token(
|
||||
|
@ -169,15 +170,17 @@ impl FxAClient for Client {
|
|||
&self,
|
||||
config: &Config,
|
||||
refresh_token: &str,
|
||||
ttl: Option<u64>,
|
||||
scopes: &[&str],
|
||||
) -> Result<OAuthTokenResponse> {
|
||||
let body = json!({
|
||||
"grant_type": "refresh_token",
|
||||
"client_id": config.client_id,
|
||||
"refresh_token": refresh_token,
|
||||
"scope": scopes.join(" ")
|
||||
});
|
||||
self.make_oauth_token_request(config, body)
|
||||
let req = OAuthTokenRequest {
|
||||
client_id: config.client_id.clone(),
|
||||
grant_type: String::from("refresh_token"),
|
||||
refresh_token: refresh_token.to_string(),
|
||||
scope: scopes.join(" "),
|
||||
ttl,
|
||||
};
|
||||
self.make_oauth_token_request(config, serde_json::to_value(req).unwrap())
|
||||
}
|
||||
|
||||
fn access_token_with_session_token(
|
||||
|
@ -658,6 +661,16 @@ pub struct DeviceResponseCommon {
|
|||
pub push_endpoint_expired: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct OAuthTokenRequest {
|
||||
pub client_id: String,
|
||||
pub grant_type: String,
|
||||
pub refresh_token: String,
|
||||
pub scope: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ttl: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OAuthTokenResponse {
|
||||
pub keys_jwe: Option<String>,
|
||||
|
|
|
@ -31,9 +31,10 @@ impl FirefoxAccount {
|
|||
/// using `begin_oauth_flow`.
|
||||
///
|
||||
/// * `scopes` - Space-separated list of requested scopes.
|
||||
/// * `ttl` - the ttl in seconds of the token requested from the server.
|
||||
///
|
||||
/// **💾 This method may alter the persisted account state.**
|
||||
pub fn get_access_token(&mut self, scope: &str) -> Result<AccessTokenInfo> {
|
||||
pub fn get_access_token(&mut self, scope: &str, ttl: Option<u64>) -> Result<AccessTokenInfo> {
|
||||
if scope.contains(' ') {
|
||||
return Err(ErrorKind::MultipleScopesRequested.into());
|
||||
}
|
||||
|
@ -48,6 +49,7 @@ impl FirefoxAccount {
|
|||
self.client.access_token_with_refresh_token(
|
||||
&self.state.config,
|
||||
&refresh_token.token,
|
||||
ttl,
|
||||
&[scope],
|
||||
)?
|
||||
} else {
|
||||
|
|
|
@ -43,7 +43,7 @@ impl FirefoxAccount {
|
|||
}
|
||||
etag = Some(cached_profile.etag.clone());
|
||||
}
|
||||
let profile_access_token = self.get_access_token(scopes::PROFILE)?.token;
|
||||
let profile_access_token = self.get_access_token(scopes::PROFILE, None)?.token;
|
||||
match self
|
||||
.client
|
||||
.profile(&self.state.config, &profile_access_token, etag)?
|
||||
|
@ -193,6 +193,7 @@ mod tests {
|
|||
mockiato::Argument::any,
|
||||
|token| token.partial_eq("refreshtok"),
|
||||
mockiato::Argument::any,
|
||||
mockiato::Argument::any,
|
||||
)
|
||||
.times(1)
|
||||
.returns_once(Ok(OAuthTokenResponse {
|
||||
|
|
|
@ -82,7 +82,7 @@ fn get_account_and_token(
|
|||
// TODO: we should probably set a persist callback on acct?
|
||||
let mut acct = load_or_create_fxa_creds(cred_file, config.clone())?;
|
||||
// `scope` could be a param, but I can't see it changing.
|
||||
match acct.get_access_token(SYNC_SCOPE) {
|
||||
match acct.get_access_token(SYNC_SCOPE, None) {
|
||||
Ok(t) => Ok((acct, t)),
|
||||
Err(e) => {
|
||||
match e.kind() {
|
||||
|
@ -90,7 +90,7 @@ fn get_account_and_token(
|
|||
error::ErrorKind::RemoteError { code: 401, .. } => {
|
||||
println!("Saw an auth error using stored credentials - recreating them...");
|
||||
acct = create_fxa_creds(cred_file, config)?;
|
||||
let token = acct.get_access_token(SYNC_SCOPE)?;
|
||||
let token = acct.get_access_token(SYNC_SCOPE, None)?;
|
||||
Ok((acct, token))
|
||||
}
|
||||
_ => Err(e.into()),
|
||||
|
|
|
@ -56,7 +56,7 @@ class MockFxAccount: FxAccount {
|
|||
invocations.append(.clearAccessTokenCache)
|
||||
}
|
||||
|
||||
override func getAccessToken(scope _: String) throws -> AccessTokenInfo {
|
||||
override func getAccessToken(scope _: String, ttl: UInt64? = nil) throws -> AccessTokenInfo {
|
||||
invocations.append(.getAccessToken)
|
||||
return AccessTokenInfo(scope: "profile", token: "toktok")
|
||||
}
|
||||
|
|
|
@ -225,7 +225,7 @@ impl TestClient {
|
|||
.expect("Failed to parse TOKENSERVER_URL environment variable!"))
|
||||
})
|
||||
.unwrap_or_else(|| self.test_acct.cfg.token_server_endpoint_url())?;
|
||||
let token = self.fxa.get_access_token(SYNC_SCOPE)?;
|
||||
let token = self.fxa.get_access_token(SYNC_SCOPE, None)?;
|
||||
|
||||
let key = token.key.as_ref().unwrap();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче