Skip to content

Commit 0fdb3b8

Browse files
committed
wip implement v2 sign message auth
1 parent 0186466 commit 0fdb3b8

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {
2+
AuthenticationFacade,
3+
AuthenticationFacadeFactory,
4+
Authenticator,
5+
Ed25519TokenBodyParser,
6+
TokenParser,
7+
} from '@dialectlabs/sdk';
8+
9+
import { SolanaSignMessageV2TokenValidator } from './solana-sign-message-v2-token-validator';
10+
import { SolanaSignMessageV2TokenGenerator } from './solana-sign-message-v2-token-generator';
11+
import type { SolanaEd25519TokenSigner } from '../ed25519/solana-ed25519-token-signer';
12+
13+
export class SolanaSignMessageV2AuthenticationFacadeFactory extends AuthenticationFacadeFactory {
14+
constructor(
15+
private readonly tokenSigner: SolanaEd25519TokenSigner,
16+
private readonly baseUrl: string,
17+
) {
18+
super();
19+
}
20+
21+
static createAuthenticator(baseUrl: string): Authenticator {
22+
return new Authenticator(
23+
new TokenParser(new Ed25519TokenBodyParser()),
24+
new SolanaSignMessageV2TokenValidator(baseUrl),
25+
);
26+
}
27+
28+
get(): AuthenticationFacade {
29+
return new AuthenticationFacade(
30+
this.tokenSigner,
31+
new SolanaSignMessageV2TokenGenerator(this.tokenSigner, this.baseUrl),
32+
SolanaSignMessageV2AuthenticationFacadeFactory.createAuthenticator(
33+
this.baseUrl,
34+
),
35+
);
36+
}
37+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
jsonParseFromBase64,
3+
type Token,
4+
type TokenSigner,
5+
} from '@dialectlabs/sdk';
6+
import { TokenGenerator } from '@dialectlabs/sdk';
7+
import axios from 'axios';
8+
import { Buffer } from 'buffer/';
9+
import { bytesFromBase64 } from '@dialectlabs/sdk/src';
10+
11+
export class SolanaSignMessageV2TokenGenerator extends TokenGenerator {
12+
constructor(signer: TokenSigner, private readonly v2BaseUrl: string) {
13+
super(signer);
14+
}
15+
16+
override async generate(ttlSeconds: number): Promise<Token> {
17+
const prepareResponse = await axios.post<{ message: string }>(
18+
`${this.v2BaseUrl}/v2/auth/solana/prepare`,
19+
{ walletAddress: this.signer.subject },
20+
);
21+
const messageToSign = prepareResponse.data.message;
22+
23+
const { signature } = await this.signer.sign(
24+
new TextEncoder().encode(messageToSign),
25+
);
26+
27+
const tokenResponse = await axios.post<{ token: string }>(
28+
`${this.v2BaseUrl}/v2/auth/solana/verify`,
29+
{
30+
message: messageToSign,
31+
signature: Buffer.from(signature).toString('base64'),
32+
},
33+
);
34+
35+
const tokenStr = tokenResponse.data.token;
36+
const [base64Header, base64Body, base64Signature] = tokenStr.split('.');
37+
if (!base64Header || !base64Body || !base64Signature) {
38+
throw new Error('Invalid token format from backend');
39+
}
40+
41+
return {
42+
rawValue: tokenStr,
43+
header: jsonParseFromBase64(base64Header),
44+
body: jsonParseFromBase64(base64Body),
45+
signature: bytesFromBase64(base64Signature),
46+
base64Header,
47+
base64Body,
48+
base64Signature,
49+
};
50+
}
51+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Token, TokenHeader } from '@dialectlabs/sdk';
2+
import { TokenValidator } from '@dialectlabs/sdk';
3+
4+
export class SolanaSignMessageV2TokenValidator extends TokenValidator {
5+
constructor(private readonly baseUrl: string) {
6+
super();
7+
}
8+
9+
override canValidate(tokenHeader: TokenHeader): boolean {
10+
return tokenHeader.typ === 'JWT' && tokenHeader.alg === 'HS256';
11+
}
12+
13+
override isSignatureValid(token: Token): boolean {
14+
return true;
15+
}
16+
}

packages/blockchain-sdk-solana/src/sdk/sdk.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@ import type {
55
Config,
66
Environment,
77
} from '@dialectlabs/sdk';
8-
import { EncryptionKeysProvider } from '@dialectlabs/sdk';
8+
import { EncryptionKeysProvider, IllegalArgumentError } from '@dialectlabs/sdk';
99
import type { DialectSolanaWalletAdapter } from '../wallet-adapter/dialect-solana-wallet-adapter.interface';
1010
import { DialectSolanaWalletAdapterEncryptionKeysProvider } from '../encryption/encryption-keys-provider';
1111
import { SolanaEd25519AuthenticationFacadeFactory } from '../auth/ed25519/solana-ed25519-authentication-facade-factory';
1212
import { DialectWalletAdapterSolanaEd25519TokenSigner } from '../auth/ed25519/solana-ed25519-token-signer';
1313
import { SolanaTxAuthenticationFacadeFactory } from '../auth/tx/solana-tx-authentication-facade-factory';
1414
import { DialectWalletAdapterSolanaTxTokenSigner } from '../auth/tx/solana-tx-token-signer';
15+
import { SolanaSignMessageV2AuthenticationFacadeFactory } from '../auth/sign-message-v2/solana-sign-message-v2-authentication-facade-factory';
1516
import { DIALECT_BLOCKCHAIN_SDK_TYPE_SOLANA } from './constants';
1617

1718
export interface SolanaConfigProps {
1819
wallet: DialectSolanaWalletAdapter;
20+
authVersion?: 1 | 2;
1921
}
2022

2123
export interface SolanaConfig extends SolanaConfigProps {
2224
wallet: DialectSolanaWalletAdapterWrapper;
25+
authVersion: 1 | 2;
2326
}
2427

2528
export type SolanaNetwork = 'mainnet-beta' | 'devnet' | 'localnet';
@@ -63,6 +66,7 @@ Solana settings:
6366
walletAdapterEncryptionKeysProvider,
6467
config.encryptionKeysStore,
6568
);
69+
6670
const authenticationFacadeFactory = wallet.canSignMessage()
6771
? new SolanaEd25519AuthenticationFacadeFactory(
6872
new DialectWalletAdapterSolanaEd25519TokenSigner(wallet),
@@ -83,12 +87,37 @@ Solana settings:
8387
};
8488
}
8589

90+
createAuthenticationFacade(solanaConfig: SolanaConfig, config: Config) {
91+
if (solanaConfig.authVersion === 1) {
92+
if (solanaConfig.wallet.canSignMessage()) {
93+
return new SolanaEd25519AuthenticationFacadeFactory(
94+
new DialectWalletAdapterSolanaEd25519TokenSigner(solanaConfig.wallet),
95+
).get();
96+
}
97+
if (solanaConfig.wallet.canSignTransaction()) {
98+
return new SolanaTxAuthenticationFacadeFactory(
99+
new DialectWalletAdapterSolanaTxTokenSigner(solanaConfig.wallet),
100+
).get();
101+
}
102+
}
103+
if (solanaConfig.authVersion === 2) {
104+
if (solanaConfig.wallet.canSignMessage()) {
105+
return new SolanaSignMessageV2AuthenticationFacadeFactory(
106+
new DialectWalletAdapterSolanaEd25519TokenSigner(solanaConfig.wallet),
107+
config.dialectCloud.v2Url,
108+
).get();
109+
}
110+
}
111+
throw new IllegalArgumentError('Unsupported auth version');
112+
}
113+
86114
private initializeSolanaConfig(): SolanaConfig {
87115
const wallet = new DialectSolanaWalletAdapterWrapper(
88116
this.solanaConfigProps.wallet,
89117
);
90118
return {
91119
wallet,
120+
authVersion: this.solanaConfigProps.authVersion ?? 2,
92121
};
93122
}
94123
}

0 commit comments

Comments
 (0)