Skip to content

Commit

Permalink
Fix scope and refresh_token for API key logins
Browse files Browse the repository at this point in the history
API key logins use a scope of `api`, not `api offline_access`. Since
`offline_access` is not requested, no `refresh_token` is returned either.
  • Loading branch information
jjlin committed Jan 21, 2022
1 parent 3f43d80 commit f99b0af
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 14 deletions.
27 changes: 15 additions & 12 deletions src/api/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
// Get device by refresh token
let mut device = Device::find_by_refresh_token(&token, &conn).map_res("Invalid refresh token")?;

// COMMON
let scope = "api offline_access";
let scope_vec = vec!["api".into(), "offline_access".into()];

// Common
let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap();
let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);

let (access_token, expires_in) = device.refresh_tokens(&user, orgs);

let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
device.save(&conn)?;

Ok(Json(json!({
"access_token": access_token,
"expires_in": expires_in,
Expand All @@ -79,7 +81,7 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
"Kdf": user.client_kdf_type,
"KdfIterations": user.client_kdf_iter,
"ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing
"scope": "api offline_access",
"scope": scope,
"unofficialServer": true,
})))
}
Expand All @@ -90,6 +92,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
if scope != "api offline_access" {
err!("Scope not supported")
}
let scope_vec = vec!["api".into(), "offline_access".into()];

// Ratelimit the login
crate::ratelimit::check_limit_login(&ip.ip)?;
Expand Down Expand Up @@ -157,8 +160,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult

// Common
let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);

let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
device.save(&conn)?;

let mut result = json!({
Expand All @@ -173,7 +175,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
"Kdf": user.client_kdf_type,
"KdfIterations": user.client_kdf_iter,
"ResetMasterPassword": false,// TODO: Same as above
"scope": "api offline_access",
"scope": scope,
"unofficialServer": true,
});

Expand All @@ -191,6 +193,7 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
if scope != "api" {
err!("Scope not supported")
}
let scope_vec = vec!["api".into()];

// Get the user via the client_id
let client_id = data.client_id.as_ref().unwrap();
Expand Down Expand Up @@ -230,24 +233,24 @@ fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult

// Common
let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn);

let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
device.save(&conn)?;

info!("User {} logged in successfully via API key. IP: {}", user.email, ip.ip);

// Note: No refresh_token is returned. The CLI just repeats the
// client_credentials login flow when the existing token expires.
Ok(Json(json!({
"access_token": access_token,
"expires_in": expires_in,
"token_type": "Bearer",
"refresh_token": device.refresh_token,
"Key": user.akey,
"PrivateKey": user.private_key,

"Kdf": user.client_kdf_type,
"KdfIterations": user.client_kdf_iter,
"ResetMasterPassword": false, // TODO: Same as above
"scope": "api",
"scope": scope,
"unofficialServer": true,
})))
}
Expand Down
9 changes: 7 additions & 2 deletions src/db/models/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ impl Device {
self.twofactor_remember = None;
}

pub fn refresh_tokens(&mut self, user: &super::User, orgs: Vec<super::UserOrganization>) -> (String, i64) {
pub fn refresh_tokens(
&mut self,
user: &super::User,
orgs: Vec<super::UserOrganization>,
scope: Vec<String>,
) -> (String, i64) {
// If there is no refresh token, we create one
if self.refresh_token.is_empty() {
use crate::crypto;
Expand Down Expand Up @@ -98,7 +103,7 @@ impl Device {

sstamp: user.security_stamp.to_string(),
device: self.uuid.to_string(),
scope: vec!["api".into(), "offline_access".into()],
scope,
amr: vec!["Application".into()],
};

Expand Down

0 comments on commit f99b0af

Please sign in to comment.