Skip to content

Commit

Permalink
SIWF v2 Process Payloads (#600)
Browse files Browse the repository at this point in the history
# Problem

We want to process the SIWF v2 payloads. Authenticate the user, and
process chain interactions.

Closes #526
Closes #527
Closes #529

# Solution

Processed the data using SIWF v2 library and then built the extrinsics
to re-use the same queue as SIWF v1.

## Steps to Verify:

1. Checkout the branch
2. Use config:
    - Testnet
    - "Provider12" Provider Id: 729
3. Start redis: `docker compose up redis`
4. Start the api `npm run start:account-api:dev`
5. Start the worker: `npm run start:account-worker:debug`
6. Start the example: `node tools/siwfv2/example.mjs`
7. Open http://localhost:3030
8. Follow the prompts!
  • Loading branch information
wilwade authored Oct 14, 2024
1 parent f781a8c commit 9a4218c
Show file tree
Hide file tree
Showing 21 changed files with 1,660 additions and 152 deletions.
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Gateway
Gateway is a suite of microservices designed to simplify interactions with the Frequency blockchain, bridging the gap between Web2 and Web3 development.

Gateway is a suite of microservices designed to simplify interactions with the Frequency blockchain, bridging the gap between Web2 and Web3 development.

<!-- TABLE OF CONTENTS -->

Expand All @@ -25,6 +25,7 @@ Gateway is a suite of microservices designed to simplify interactions with the F
Gateway is a collection of services designed to simplify interactions with the Frequency blockchain. It bridges the gap between Web2 and Web3, allowing developers to interact with Frequency as easily as they would with any traditional Web2 API.

Key features:

- Modular architecture: Use each service independently to fit your specific needs
- Simplified blockchain interactions: Abstract away complex blockchain operations
- Web2-like experience: Familiar REST API patterns for Web2 developers
Expand All @@ -34,6 +35,7 @@ For a practical implementation example, check out our **Social App Template** [h
<p align="right">(<a href="#-table-of-contents">back to top</a>)</p>

## 🚀 Live Docs <a name="-live-docs"></a>

Visit our Live API Documentation to start exploring the Gateway services.

**[Access Our Live API Docs Here](https://projectlibertylabs.github.io/gateway/)**
Expand Down Expand Up @@ -76,12 +78,12 @@ flowchart LR;

Gateway consists of four independent microservices, each designed to handle specific aspects of interaction with the Frequency blockchain. Below is a detailed overview of each service:

| Service | Description | API Documentation | README |
|---------|-------------|-------------------|--------|
| Account Service | Manages user accounts and authentication on the Frequency blockchain using [Sign In With Frequency](https://github.com/ProjectLibertyLabs/siwf). It handles tasks such as account creation and key management. | [API Docs](https://projectlibertylabs.github.io/gateway/account) | [README](./developer-docs/account/README.md) |
| Graph Service | Manages social connections and relationships between users on the Frequency network. It handles operations like following/unfollowing users and retrieving social graphs. | [API Docs](https://projectlibertylabs.github.io/gateway/graph/) | [README](./developer-docs/graph/README.md) |
| Content Publishing Service | Facilitates the creation and publication of content on the Frequency blockchain. It manages tasks such as posting messages, attachments, replies, and reactions. | [API Docs](https://projectlibertylabs.github.io/gateway/content-publishing/) | [README](./developer-docs/content-publishing/README.md) |
| Content Watcher Service | Monitors and retrieves content updates from the Frequency blockchain. It allows applications to efficiently track new content as it's published. | [API Docs](https://projectlibertylabs.github.io/gateway/content-watcher/) | [README](./developer-docs/content-watcher/README.md) |
| Service | Description | API Documentation | README |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------- |
| Account Service | Manages user accounts and authentication on the Frequency blockchain using [Sign In With Frequency](https://github.com/ProjectLibertyLabs/siwf). It handles tasks such as account creation and key management. | [API Docs](https://projectlibertylabs.github.io/gateway/account) | [README](./developer-docs/account/README.md) |
| Graph Service | Manages social connections and relationships between users on the Frequency network. It handles operations like following/unfollowing users and retrieving social graphs. | [API Docs](https://projectlibertylabs.github.io/gateway/graph/) | [README](./developer-docs/graph/README.md) |
| Content Publishing Service | Facilitates the creation and publication of content on the Frequency blockchain. It manages tasks such as posting messages, attachments, replies, and reactions. | [API Docs](https://projectlibertylabs.github.io/gateway/content-publishing/) | [README](./developer-docs/content-publishing/README.md) |
| Content Watcher Service | Monitors and retrieves content updates from the Frequency blockchain. It allows applications to efficiently track new content as it's published. | [API Docs](https://projectlibertylabs.github.io/gateway/content-watcher/) | [README](./developer-docs/content-watcher/README.md) |

<p align="right">(<a href="#-table-of-contents">back to top</a>)</p>

Expand All @@ -94,24 +96,24 @@ Gateway consists of four independent microservices, each designed to handle spec

<p align="right">(<a href="#-table-of-contents">back to top</a>)</p>


<!-- GETTING STARTED -->

## 💻 Getting Started <a name="getting-started"></a>

Gateway offers flexibility in how you can use its services. You can either quickly set up all services or selectively use specific microservices based on your needs. Below, we provide both a Quick Start guide for setting up all services and information on how to get started with individual microservices.


### 🚀 Quick Start Guide <a name="quick-start-guide"></a>

Follow these steps to quickly get all Gateway services up and running:

1. **Prerequisites**:
In order to run this project you need:
- [Docker](https://www.docker.com) or Docker compatible layer for running Gateway Services
- [mdBook](https://rust-lang.github.io/mdBook/) for building documentation
In order to run this project you need:

- [Docker](https://www.docker.com) or Docker compatible layer for running Gateway Services
- [mdBook](https://rust-lang.github.io/mdBook/) for building documentation

2. **Clone the Repository**:

```sh
git clone [email protected]:ProjectLibertyLabs/gateway.git
cd gateway
Expand All @@ -122,13 +124,15 @@ In order to run this project you need:
```sh
./start.sh
```

This script will start all Gateway microservices using Docker.

4. **Stop all Services**

```sh
./stop.sh
```

Use this script when you need to stop all running Gateway services.

5. **Build Gateway Documentation**
Expand Down
10 changes: 10 additions & 0 deletions apps/account-api/src/api.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ describe('Account API Config', () => {
SIWF_NODE_RPC_URL: undefined,
GRAPH_ENVIRONMENT_TYPE: undefined,
SIWF_URL: undefined,
SIWF_V2_URL: undefined,
SIWF_V2_DOMAIN: undefined,
};

beforeAll(() => {
Expand All @@ -37,6 +39,14 @@ describe('Account API Config', () => {
it('invalid api body json limit should fail', async () => shouldFailBadValues(ALL_ENV, 'API_BODY_JSON_LIMIT', [0]));

it('invalid api timeout limit should fail', async () => shouldFailBadValues(ALL_ENV, 'API_TIMEOUT_MS', [0]));

it('invalid url for SIWF_V2_URL', async () => shouldFailBadValues(ALL_ENV, 'SIWF_V2_URL', ['sdfdsf']));

it('invalid url SIWF_V2_DOMAIN', async () =>
shouldFailBadValues(ALL_ENV, 'SIWF_V2_DOMAIN', ['https://www.example.com']));

it('invalid with port SIWF_V2_DOMAIN', async () =>
shouldFailBadValues(ALL_ENV, 'SIWF_V2_DOMAIN', ['localhost:3000']));
});

describe('valid environment', () => {
Expand Down
6 changes: 6 additions & 0 deletions apps/account-api/src/api.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface IAccountApiConfig {
graphEnvironmentType: keyof EnvironmentType;
siwfUrl: string;
siwfV2Url?: string;
siwfV2Domain?: string;
}

export default registerAs('account-api', (): IAccountApiConfig => {
Expand Down Expand Up @@ -43,6 +44,11 @@ export default registerAs('account-api', (): IAccountApiConfig => {
value: process.env.SIWF_V2_URL,
joi: Joi.string().optional().allow(null).allow('').empty('').uri(),
},
siwfV2Domain: {
value: process.env.SIWF_V2_DOMAIN,
// Allow localhost specifically
joi: Joi.string().optional().allow(null).allow('localhost').allow('').empty('').domain(),
},
};

return JoiUtils.validate<IAccountApiConfig>(configs);
Expand Down
55 changes: 52 additions & 3 deletions apps/account-api/src/controllers/v2/accounts-v2.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { SiwfV2Service } from '#account-api/services/siwfV2.service';
import blockchainConfig, { IBlockchainConfig } from '#blockchain/blockchain.config';
import { SCHEMA_NAME_TO_ID } from '#types/constants/schemas';
import { WalletV2LoginRequestDto } from '#types/dtos/account/wallet.v2.login.request.dto';
import { WalletV2LoginResponseDto } from '#types/dtos/account/wallet.v2.login.response.dto';
import { WalletV2RedirectRequestDto } from '#types/dtos/account/wallet.v2.redirect.request.dto';
import { WalletV2RedirectResponseDto } from '#types/dtos/account/wallet.v2.redirect.response.dto';
import { Controller, HttpCode, HttpStatus, Logger, Query, Get } from '@nestjs/common';
import {
Controller,
HttpCode,
HttpStatus,
Logger,
Query,
Get,
Post,
Body,
ForbiddenException,
Inject,
} from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { hasChainSubmissions } from '@projectlibertylabs/siwfv2';

// # SIWF Wallet API V2
// Goal: Generic Interface that supports FA and dApp 3rd-party wallets
Expand Down Expand Up @@ -40,7 +56,11 @@ import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
export class AccountsControllerV2 {
private readonly logger: Logger;

constructor(private siwfV2Service: SiwfV2Service) {
constructor(
private siwfV2Service: SiwfV2Service,
@Inject(blockchainConfig.KEY) private chainConfig: IBlockchainConfig,
@Inject(apiConfig.KEY) private accountConfig: IAccountApiConfig,
) {
this.logger = new Logger(this.constructor.name);
}

Expand All @@ -50,12 +70,41 @@ export class AccountsControllerV2 {
@ApiOperation({ summary: 'Get the Sign In With Frequency Redirect URL' })
@ApiOkResponse({ description: 'SIWF Redirect URL', type: WalletV2RedirectResponseDto })
async getRedirectUrl(@Query() query: WalletV2RedirectRequestDto): Promise<WalletV2RedirectResponseDto> {
this.logger.debug('Received request for Sign In With Frequency v2 Redirect URL', query);
this.logger.debug('Received request for Sign In With Frequency v2 Redirect URL', JSON.stringify(query));

const { callbackUrl } = query;
const permissions = (query.permissions || []).map((p) => SCHEMA_NAME_TO_ID.get(p));
const credentials = query.credentials || [];

return this.siwfV2Service.getRedirectUrl(callbackUrl, permissions, credentials);
}

@Post('siwf')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Process the result of a Sign In With Frequency v2 callback' })
@ApiOkResponse({ description: 'Signed in successfully', type: WalletV2LoginResponseDto })
async postSignInWithFrequency(@Body() callbackRequest: WalletV2LoginRequestDto): Promise<WalletV2LoginResponseDto> {
this.logger.debug('Received Sign In With Frequency v2 callback', JSON.stringify(callbackRequest));

if (!this.accountConfig.siwfV2Domain) {
this.logger.error('"SIWF_V2_DOMAIN" required to use SIWF v2');
throw new ForbiddenException('SIWF v2 processing unavailable');
}

// Extract a valid payload from the request
// This inludes the validation of the login payload if any
// Also makes sure it is either a login or a delegation
const payload = await this.siwfV2Service.getPayload(callbackRequest);

if (hasChainSubmissions(payload) && this.chainConfig.isDeployedReadOnly) {
throw new ForbiddenException('New account sign-up unavailable in read-only mode');
}

// Trigger chain submissions, if any
await this.siwfV2Service.queueChainActions(payload);

const response = this.siwfV2Service.getSiwfV2LoginResponse(payload);

return response;
}
}
9 changes: 7 additions & 2 deletions apps/account-api/src/services/accounts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import { BadRequestException, Inject, Injectable, Logger, UnprocessableEntityExc
import { validateSignin, validateSignup } from '@projectlibertylabs/siwfv1';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';
import { EnqueueService } from '#account-lib/services/enqueue-request.service';
import { PublishSIWFSignupRequestDto, WalletLoginRequestDto } from '#types/dtos/account/wallet.login.request.dto';
import { WalletLoginRequestDto } from '#types/dtos/account/wallet.login.request.dto';
import { WalletLoginResponseDto } from '#types/dtos/account/wallet.login.response.dto';
import {
AccountResponseDto,
MsaIdResponseDto,
RetireMsaPayloadResponseDto,
} from '#types/dtos/account/accounts.response.dto';
import { WalletLoginConfigResponseDto } from '#types/dtos/account/wallet.login.config.response.dto';
import { PublishRetireMsaRequestDto, RetireMsaRequestDto, TransactionResponse } from '#types/dtos/account';
import {
PublishRetireMsaRequestDto,
PublishSIWFSignupRequestDto,
RetireMsaRequestDto,
TransactionResponse,
} from '#types/dtos/account';
import { TransactionType } from '#types/account-webhook';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import blockchainConfig, { IBlockchainConfig } from '#blockchain/blockchain.config';
Expand Down
Loading

0 comments on commit 9a4218c

Please sign in to comment.