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:
Edouard Oger 2020-04-08 11:54:31 -04:00 коммит произвёл GitHub
Родитель 961607cf75 1ecf3cb952
Коммит f39ad2f44f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 60 добавлений и 26 удалений

Просмотреть файл

@ -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();