diff --git a/services/identity/src/database/farcaster.rs b/services/identity/src/database/farcaster.rs --- a/services/identity/src/database/farcaster.rs +++ b/services/identity/src/database/farcaster.rs @@ -95,6 +95,26 @@ Ok(()) } + + pub async fn remove_farcaster_id( + &self, + user_id: String, + ) -> Result<(), Error> { + let update_expression = + format!("REMOVE {}", USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME); + + self + .client + .update_item() + .table_name(USERS_TABLE) + .key(USERS_TABLE_PARTITION_KEY, AttributeValue::S(user_id)) + .update_expression(update_expression) + .send() + .await + .map_err(|e| Error::AwsSdk(e.into()))?; + + Ok(()) + } } impl TryFrom for FarcasterUserData { diff --git a/services/identity/src/grpc_services/authenticated.rs b/services/identity/src/grpc_services/authenticated.rs --- a/services/identity/src/grpc_services/authenticated.rs +++ b/services/identity/src/grpc_services/authenticated.rs @@ -433,6 +433,22 @@ let response = Empty {}; Ok(Response::new(response)) } + + async fn unlink_farcaster_account( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let (user_id, _) = get_user_and_device_id(&request)?; + + self + .db_client + .remove_farcaster_id(user_id) + .await + .map_err(handle_db_error)?; + + let response = Empty {}; + Ok(Response::new(response)) + } } // raw device list that can be serialized to JSON (and then signed in the future) diff --git a/shared/protos/identity_auth.proto b/shared/protos/identity_auth.proto --- a/shared/protos/identity_auth.proto +++ b/shared/protos/identity_auth.proto @@ -63,6 +63,9 @@ // Called by an existing user to link their Farcaster account rpc LinkFarcasterAccount(LinkFarcasterAccountRequest) returns (identity.unauth.Empty) {} + // Called by an existing user to unlink their Farcaster account + rpc UnlinkFarcasterAccount(identity.unauth.Empty) returns + (identity.unauth.Empty) {} } // Helper types diff --git a/web/protobufs/identity-auth-client.cjs b/web/protobufs/identity-auth-client.cjs --- a/web/protobufs/identity-auth-client.cjs +++ b/web/protobufs/identity-auth-client.cjs @@ -810,4 +810,65 @@ }; +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.identity.unauth.Empty, + * !proto.identity.unauth.Empty>} + */ +const methodDescriptor_IdentityClientService_UnlinkFarcasterAccount = new grpc.web.MethodDescriptor( + '/identity.auth.IdentityClientService/UnlinkFarcasterAccount', + grpc.web.MethodType.UNARY, + identity_unauth_pb.Empty, + identity_unauth_pb.Empty, + /** + * @param {!proto.identity.unauth.Empty} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + identity_unauth_pb.Empty.deserializeBinary +); + + +/** + * @param {!proto.identity.unauth.Empty} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.identity.unauth.Empty)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.identity.auth.IdentityClientServiceClient.prototype.unlinkFarcasterAccount = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/identity.auth.IdentityClientService/UnlinkFarcasterAccount', + request, + metadata || {}, + methodDescriptor_IdentityClientService_UnlinkFarcasterAccount, + callback); +}; + + +/** + * @param {!proto.identity.unauth.Empty} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.identity.auth.IdentityClientServicePromiseClient.prototype.unlinkFarcasterAccount = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/identity.auth.IdentityClientService/UnlinkFarcasterAccount', + request, + metadata || {}, + methodDescriptor_IdentityClientService_UnlinkFarcasterAccount); +}; + + module.exports = proto.identity.auth; diff --git a/web/protobufs/identity-auth-client.cjs.flow b/web/protobufs/identity-auth-client.cjs.flow --- a/web/protobufs/identity-auth-client.cjs.flow +++ b/web/protobufs/identity-auth-client.cjs.flow @@ -94,6 +94,13 @@ callback: (err: grpcWeb.RpcError, response: identityStructs.Empty) => void ): grpcWeb.ClientReadableStream; + + unlinkFarcasterAccount( + request: identityStructs.Empty, + metadata: grpcWeb.Metadata | void, + callback: (err: grpcWeb.RpcError, + response: identityStructs.Empty) => void + ): grpcWeb.ClientReadableStream; } declare export class IdentityClientServicePromiseClient { @@ -160,4 +167,9 @@ request: identityAuthStructs.LinkFarcasterAccountRequest, metadata?: grpcWeb.Metadata ): Promise; + + unlinkFarcasterAccount( + request: identityStructs.Empty, + metadata?: grpcWeb.Metadata + ): Promise; }