Skip to content

Commit bc90445

Browse files
committed
feat: add logical flow to decide if solana auth is used for login/link/register/verify
1 parent 9f6d892 commit bc90445

6 files changed

+115
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { prisma } from '~/lib/db.server'
2+
import { IdentityProvider } from '@prisma/client'
3+
4+
export async function getUserByIdentity({ provider, providerId }: { provider: IdentityProvider; providerId: string }) {
5+
return await prisma.user.findFirst({
6+
include: { identities: true },
7+
where: { identities: { some: { provider, providerId } } },
8+
})
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { isValidSolanaPubKey } from '~/lib/solana/is-valid-solana-pubkey'
2+
import { IdentityProvider } from '@prisma/client'
3+
import { getUserByIdentity } from '~/features/auth/data-access/get-user-by-identity'
4+
5+
export async function getUserBySolanaIdentity({ providerId }: { providerId: string }) {
6+
if (!isValidSolanaPubKey(providerId)) {
7+
throw new Error('Invalid Solana providerId')
8+
}
9+
return getUserByIdentity({ provider: IdentityProvider.Solana, providerId })
10+
}

app/features/pubkey/pubkey-feature-profile-create.tsx

+2-9
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import { UiCard } from '~/ui/ui-card'
1212
import { PubkeyUiProfileCreateForm } from './ui/pubkey-ui-profile-create-form'
1313
import { getPubkeySdkCommunity } from '~/lib/pubkey/get-pubkey-sdk-community'
1414
import { useConnection, useWallet } from '@solana/wallet-adapter-react'
15-
import { PublicKey, VersionedTransaction } from '@solana/web3.js'
15+
import { VersionedTransaction } from '@solana/web3.js'
1616
import { base58 } from '@metaplex-foundation/umi'
17+
import { isValidSolanaPubKey } from '~/lib/solana/is-valid-solana-pubkey'
1718

1819
export function meta() {
1920
return appMeta('Profiles')
@@ -114,14 +115,6 @@ export default function PubkeyFeatureCommunityCreate({ loaderData: { config } }:
114115
)
115116
}
116117

117-
function isValidSolanaPubKey(address: string) {
118-
try {
119-
return !!new PublicKey(address)
120-
} catch {
121-
return false
122-
}
123-
}
124-
125118
function txToBase58(tx: VersionedTransaction): string {
126119
const [res] = base58.deserialize(tx.serialize())
127120
return res
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export enum SolanaVerificationType {
2+
Error = 'Error',
3+
Login = 'Login',
4+
Register = 'Register',
5+
Link = 'Link',
6+
Verify = 'Verify',
7+
}
8+
9+
export function getSolanaVerificationType({
10+
actorId,
11+
ownerId,
12+
enabledTypes = [
13+
SolanaVerificationType.Login,
14+
SolanaVerificationType.Link,
15+
SolanaVerificationType.Register,
16+
SolanaVerificationType.Verify,
17+
],
18+
}: {
19+
actorId?: string
20+
ownerId?: string
21+
enabledTypes?: SolanaVerificationType[]
22+
}): {
23+
message?: string
24+
type: SolanaVerificationType
25+
} {
26+
function ensureEnabledType(type: SolanaVerificationType) {
27+
if (!enabledTypes.includes(type)) {
28+
throw new Error(`Solana verification type ${type} is not enabled`)
29+
}
30+
return type
31+
}
32+
33+
// If the wallet isn't owned by any user
34+
if (!ownerId) {
35+
return actorId
36+
? // If actor is set, we want to link the wallet
37+
{ type: ensureEnabledType(SolanaVerificationType.Link) }
38+
: // Otherwise, we want to register a new user
39+
{ type: ensureEnabledType(SolanaVerificationType.Register) }
40+
}
41+
42+
// We are not logged in, so this is a login
43+
if (!actorId) {
44+
return { type: ensureEnabledType(SolanaVerificationType.Login) }
45+
}
46+
47+
// We are logged in, make sure that the user owns the wallet
48+
if (ownerId !== actorId) {
49+
return { type: SolanaVerificationType.Error, message: 'User does not own public key' }
50+
}
51+
52+
// Actor owns the wallet, we can now verify it
53+
return { type: ensureEnabledType(SolanaVerificationType.Verify) }
54+
}

app/features/solana/user-solana-wallet.tsx

+31-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import type { Route } from './+types/user-solana-wallet'
55
import { useWallet } from '@solana/wallet-adapter-react'
66
import { solanaAuth } from '~/lib/solana-auth/solana-auth'
77
import type { SolanaAuthMessage, SolanaAuthMessageSigned } from '~/lib/solana-auth/solana-auth-message'
8+
import { getUserBySolanaIdentity } from '~/features/auth/data-access/get-user-by-solana-identity'
9+
import { getUser } from '~/features/auth/data-access/get-user'
10+
import { getSolanaVerificationType, SolanaVerificationType } from './get-solana-verification-type'
811

912
function parsePayload(payload: string = ''): SolanaAuthMessageSigned {
1013
try {
@@ -23,8 +26,27 @@ export async function action({ request }: Route.LoaderArgs) {
2326
if (!publicKey) {
2427
return { success: false, message: `No public key` }
2528
}
29+
const actor = await getUser(request)
30+
const owner = await getUserBySolanaIdentity({ providerId: publicKey })
31+
32+
// This determines the type of verification we are performing
33+
const verification = getSolanaVerificationType({
34+
actorId: actor?.id ?? undefined,
35+
ownerId: owner?.id ?? undefined,
36+
enabledTypes: [
37+
SolanaVerificationType.Login,
38+
SolanaVerificationType.Link,
39+
SolanaVerificationType.Register,
40+
SolanaVerificationType.Verify,
41+
],
42+
})
43+
if (verification.type === SolanaVerificationType.Error) {
44+
return { success: false, message: verification.message }
45+
}
2646

27-
console.log(`user-solana-wallet -> action`, action, 'publicKey', publicKey)
47+
console.log(
48+
`user-solana-wallet [${action}] -> publicKey: ${publicKey} -> owner: ${owner ? owner.username : 'NONE'} -> type: ${verification.type}`,
49+
)
2850

2951
switch (formData.get('action')) {
3052
case 'sign-message-create':
@@ -34,16 +56,14 @@ export async function action({ request }: Route.LoaderArgs) {
3456
type: 'solana-auth-message',
3557
}
3658
case 'sign-message-verify':
37-
const { message, signature, blockhash, nonce } = parsePayload(payload)
38-
console.log(`sign message -> verify`, 'message', message, 'signature', signature, 'blockhash', blockhash)
39-
const result = await solanaAuth.verifyMessage({
40-
method: 'solana:signMessage',
41-
publicKey,
42-
message,
43-
signature,
44-
blockhash,
45-
nonce,
46-
})
59+
const parsed = parsePayload(payload)
60+
const result = await solanaAuth.verifyMessage(parsed)
61+
if (!result) {
62+
throw new Error('Invalid signature')
63+
}
64+
console.log(`sign message -> verify`, 'message', parsed, 'signature', parsed.signature, 'result', result)
65+
// TODO: Wallet is verified. We can now:
66+
// - Check if the wallet exists
4767
return {
4868
success: true,
4969
message: result,
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { PublicKey } from '@solana/web3.js'
2+
3+
export function isValidSolanaPubKey(address: string) {
4+
try {
5+
return !!new PublicKey(address)
6+
} catch {
7+
return false
8+
}
9+
}

0 commit comments

Comments
 (0)