Support "authorization_code" OAuth grant type
This commit is contained in:
parent
cf69f1394a
commit
fdd3a22807
5 changed files with 53 additions and 1 deletions
|
@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
- Added `/api/v1/apps` endpoint.
|
- Added `/api/v1/apps` endpoint.
|
||||||
- Added OAuth authorization page.
|
- Added OAuth authorization page.
|
||||||
|
- Support `authorization_code` OAuth grant type.
|
||||||
- Documented `http_cors_allowlist` configuration parameter.
|
- Documented `http_cors_allowlist` configuration parameter.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -18,9 +18,15 @@ paths:
|
||||||
grant_type:
|
grant_type:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
- authorization_code
|
||||||
- password
|
- password
|
||||||
- eip4361
|
- eip4361
|
||||||
example: eip4361
|
example: eip4361
|
||||||
|
code:
|
||||||
|
description: A user authorization code, obtained via GET /oauth/authorize (required if grant type is "authorization_code").
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
example: null
|
||||||
username:
|
username:
|
||||||
description: User name (required if grant type is "password").
|
description: User name (required if grant type is "password").
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -18,6 +18,11 @@ pub struct AuthorizationQueryParams {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct TokenRequest {
|
pub struct TokenRequest {
|
||||||
pub grant_type: String,
|
pub grant_type: String,
|
||||||
|
|
||||||
|
// Required if grant type is "authorization_code"
|
||||||
|
pub code: Option<String>,
|
||||||
|
|
||||||
|
// Required if grant type is "password" or "eip4361"
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
pub wallet_address: Option<String>,
|
pub wallet_address: Option<String>,
|
||||||
// Required only with "password" and "ethereum" grant types
|
// Required only with "password" and "ethereum" grant types
|
||||||
|
|
|
@ -2,6 +2,7 @@ use actix_web::{
|
||||||
get,
|
get,
|
||||||
post,
|
post,
|
||||||
web,
|
web,
|
||||||
|
Either,
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
Scope as ActixScope,
|
Scope as ActixScope,
|
||||||
http::header as http_header,
|
http::header as http_header,
|
||||||
|
@ -17,6 +18,7 @@ use crate::models::oauth::queries::{
|
||||||
create_oauth_authorization,
|
create_oauth_authorization,
|
||||||
delete_oauth_token,
|
delete_oauth_token,
|
||||||
get_oauth_app_by_client_id,
|
get_oauth_app_by_client_id,
|
||||||
|
get_user_by_authorization_code,
|
||||||
save_oauth_token,
|
save_oauth_token,
|
||||||
};
|
};
|
||||||
use crate::models::users::queries::{
|
use crate::models::users::queries::{
|
||||||
|
@ -108,10 +110,25 @@ const ACCESS_TOKEN_EXPIRES_IN: i64 = 86400 * 7;
|
||||||
async fn token_view(
|
async fn token_view(
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
db_pool: web::Data<DbPool>,
|
db_pool: web::Data<DbPool>,
|
||||||
request_data: web::Json<TokenRequest>,
|
request_data: Either<
|
||||||
|
web::Json<TokenRequest>,
|
||||||
|
web::Form<TokenRequest>,
|
||||||
|
>,
|
||||||
) -> Result<HttpResponse, HttpError> {
|
) -> Result<HttpResponse, HttpError> {
|
||||||
|
let request_data = match request_data {
|
||||||
|
Either::Left(json) => json.into_inner(),
|
||||||
|
Either::Right(form) => form.into_inner(),
|
||||||
|
};
|
||||||
let db_client = &**get_database_client(&db_pool).await?;
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
let user = match request_data.grant_type.as_str() {
|
let user = match request_data.grant_type.as_str() {
|
||||||
|
"authorization_code" => {
|
||||||
|
let authorization_code = request_data.code.as_ref()
|
||||||
|
.ok_or(ValidationError("authorization code is required"))?;
|
||||||
|
get_user_by_authorization_code(
|
||||||
|
db_client,
|
||||||
|
authorization_code,
|
||||||
|
).await?
|
||||||
|
},
|
||||||
"password" => {
|
"password" => {
|
||||||
let username = request_data.username.as_ref()
|
let username = request_data.username.as_ref()
|
||||||
.ok_or(ValidationError("username is required"))?;
|
.ok_or(ValidationError("username is required"))?;
|
||||||
|
|
|
@ -90,6 +90,29 @@ pub async fn create_oauth_authorization(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_by_authorization_code(
|
||||||
|
db_client: &impl DatabaseClient,
|
||||||
|
authorization_code: &str,
|
||||||
|
) -> Result<User, DatabaseError> {
|
||||||
|
let maybe_row = db_client.query_opt(
|
||||||
|
"
|
||||||
|
SELECT user_account, actor_profile
|
||||||
|
FROM oauth_authorization
|
||||||
|
JOIN user_account ON oauth_authorization.user_id = user_account.id
|
||||||
|
JOIN actor_profile ON user_account.id = actor_profile.id
|
||||||
|
WHERE
|
||||||
|
oauth_authorization.code = $1
|
||||||
|
AND oauth_authorization.expires_at > CURRENT_TIMESTAMP
|
||||||
|
",
|
||||||
|
&[&authorization_code],
|
||||||
|
).await?;
|
||||||
|
let row = maybe_row.ok_or(DatabaseError::NotFound("authorization"))?;
|
||||||
|
let db_user: DbUser = row.try_get("user_account")?;
|
||||||
|
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
||||||
|
let user = User::new(db_user, db_profile);
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn save_oauth_token(
|
pub async fn save_oauth_token(
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
owner_id: &Uuid,
|
owner_id: &Uuid,
|
||||||
|
|
Loading…
Reference in a new issue