From 356592bc7196f578b2012058f405e454e442194d Mon Sep 17 00:00:00 2001 From: silverpill Date: Fri, 22 Apr 2022 10:10:54 +0000 Subject: [PATCH] Add views for generating address ownership proof --- docs/openapi.yaml | 41 +++++++++++++++++++++++++ src/mastodon_api/accounts/types.rs | 10 ++++++ src/mastodon_api/accounts/views.rs | 49 ++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 53d92f4..d5fce62 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -8,6 +8,7 @@ paths: /oauth/token: post: summary: Returns an access token, to be used during API calls that are not public. + description: EIP-4361 auth doesn't require blockchain integration. requestBody: content: application/json: @@ -102,6 +103,46 @@ paths: $ref: '#/components/schemas/Account' 400: description: Invalid user data + /api/v1/accounts/identity_proof: + get: + summary: Get unsigned data for identity proof. + responses: + 200: + description: Successful operation + content: + application/json: + schema: + type: object + properties: + claim: + description: Identity claim serialized as compact JSON. + type: string + example: '{"id":"https://example.com/users/1","ownerOf":"did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a"}' + 403: + description: User's wallet address is not known + post: + summary: Submit identity proof. + requestBody: + content: + application/json: + schema: + type: object + properties: + signature: + description: Signature value. + type: string + example: '3312dacd...' + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + 400: + description: Invalid signature. + 403: + description: User's wallet address is not known /api/v1/accounts/authorize_subscription: get: summary: Get authorization for setting up paid subscription. diff --git a/src/mastodon_api/accounts/types.rs b/src/mastodon_api/accounts/types.rs index b847691..8f9823d 100644 --- a/src/mastodon_api/accounts/types.rs +++ b/src/mastodon_api/accounts/types.rs @@ -185,6 +185,16 @@ impl AccountUpdateData { } } +#[derive(Serialize)] +pub struct IdentityClaim { + pub claim: String, +} + +#[derive(Deserialize)] +pub struct IdentityProofData { + pub signature: String, +} + // TODO: actix currently doesn't support parameter arrays // https://github.com/actix/actix-web/issues/2044 #[derive(Deserialize)] diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index e782720..77fb877 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -14,6 +14,11 @@ use crate::database::{Pool, get_database_client}; use crate::errors::{DatabaseError, HttpError, ValidationError}; use crate::ethereum::eip4361::verify_eip4361_signature; use crate::ethereum::gate::is_allowed_user; +use crate::ethereum::identity::{ + DidPkh, + create_identity_claim, + verify_identity_proof, +}; use crate::ethereum::subscriptions::create_subscription_signature; use crate::mastodon_api::oauth::auth::get_current_user; use crate::models::posts::helpers::{ @@ -57,6 +62,8 @@ use super::types::{ AccountUpdateData, FollowData, FollowListQueryParams, + IdentityClaim, + IdentityProofData, RelationshipQueryParams, StatusListQueryParams, }; @@ -213,6 +220,46 @@ async fn update_credentials( Ok(HttpResponse::Ok().json(account)) } +#[get("/identity_proof")] +async fn get_identity_claim( + auth: BearerAuth, + config: web::Data, + db_pool: web::Data, +) -> Result { + let db_client = &**get_database_client(&db_pool).await?; + let current_user = get_current_user(db_client, auth.token()).await?; + let actor_id = current_user.profile.actor_id(&config.instance_url()); + let wallet_address = current_user.wallet_address.as_ref() + .ok_or(HttpError::PermissionError)?; + let did = DidPkh::from_ethereum_address(wallet_address); + let claim = create_identity_claim(&actor_id, &did) + .map_err(|_| HttpError::InternalError)?; + let response = IdentityClaim { claim }; + Ok(HttpResponse::Ok().json(response)) +} + +#[post("/identity_proof")] +async fn create_identity_proof( + auth: BearerAuth, + config: web::Data, + db_pool: web::Data, + proof_data: web::Json, +) -> Result { + let db_client = &**get_database_client(&db_pool).await?; + let current_user = get_current_user(db_client, auth.token()).await?; + let actor_id = current_user.profile.actor_id(&config.instance_url()); + let wallet_address = current_user.wallet_address.as_ref() + .ok_or(HttpError::PermissionError)?; + let did = DidPkh::from_ethereum_address(wallet_address); + verify_identity_proof( + &actor_id, + &did, + &proof_data.signature, + )?; + let account = Account::from_user(current_user, &config.instance_url()); + Ok(HttpResponse::Ok().json(account)) +} + #[get("/authorize_subscription")] async fn authorize_subscription( auth: BearerAuth, @@ -455,6 +502,8 @@ pub fn account_api_scope() -> Scope { .service(get_relationships_view) .service(verify_credentials) .service(update_credentials) + .service(get_identity_claim) + .service(create_identity_proof) .service(authorize_subscription) // Routes with account ID .service(get_account)