Support "authorization_code" OAuth grant type

This commit is contained in:
silverpill 2023-02-11 17:54:38 +00:00
parent cf69f1394a
commit fdd3a22807
5 changed files with 53 additions and 1 deletions

View file

@ -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 OAuth authorization page.
- Support `authorization_code` OAuth grant type.
- Documented `http_cors_allowlist` configuration parameter.
### Changed

View file

@ -18,9 +18,15 @@ paths:
grant_type:
type: string
enum:
- authorization_code
- password
- 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:
description: User name (required if grant type is "password").
type: string

View file

@ -18,6 +18,11 @@ pub struct AuthorizationQueryParams {
#[derive(Deserialize)]
pub struct TokenRequest {
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 wallet_address: Option<String>,
// Required only with "password" and "ethereum" grant types

View file

@ -2,6 +2,7 @@ use actix_web::{
get,
post,
web,
Either,
HttpResponse,
Scope as ActixScope,
http::header as http_header,
@ -17,6 +18,7 @@ use crate::models::oauth::queries::{
create_oauth_authorization,
delete_oauth_token,
get_oauth_app_by_client_id,
get_user_by_authorization_code,
save_oauth_token,
};
use crate::models::users::queries::{
@ -108,10 +110,25 @@ const ACCESS_TOKEN_EXPIRES_IN: i64 = 86400 * 7;
async fn token_view(
config: web::Data<Config>,
db_pool: web::Data<DbPool>,
request_data: web::Json<TokenRequest>,
request_data: Either<
web::Json<TokenRequest>,
web::Form<TokenRequest>,
>,
) -> 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 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" => {
let username = request_data.username.as_ref()
.ok_or(ValidationError("username is required"))?;

View file

@ -90,6 +90,29 @@ pub async fn create_oauth_authorization(
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(
db_client: &impl DatabaseClient,
owner_id: &Uuid,