Skip to content

Commit ccf5155

Browse files
committed
feat: add proper OAuth, update examples
1 parent 2e349f2 commit ccf5155

File tree

12 files changed

+746
-647
lines changed

12 files changed

+746
-647
lines changed

examples/turnkey/app/src/App.vue

+25-53
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,27 @@ import { BaseWalletStrategy, MsgBroadcaster } from '@injectivelabs/wallet-core'
33
import {
44
TurnkeyWallet,
55
TurnkeyWalletStrategy,
6-
type TurnkeyStatus,
76
} from '@injectivelabs/wallet-turnkey/src/index.ts'
87
import { injectiveClients } from './injective-clients'
9-
import { computed, onMounted, ref, watch } from 'vue'
8+
import { computed, onMounted, watch } from 'vue'
109
import { Wallet } from '@injectivelabs/wallet-base'
1110
import LoginForm from './components/LoginForm.vue'
1211
import Connected from './components/Connected.vue'
1312
import {
14-
getEthereumAddress,
15-
type Msgs,
16-
type TxResponse,
17-
} from '@injectivelabs/sdk-ts'
18-
const turnkeyAuthIframeContainerId = 'turnkey-auth-iframe-container-id'
19-
20-
const turnkeyStrategy = ref<TurnkeyWalletStrategy | null>(null)
21-
const broadcaster = ref<MsgBroadcaster | null>(null)
22-
const turnkeyStatus = ref<TurnkeyStatus | null>(null)
23-
const address = ref<string | null>(null)
13+
turnkeyStatus,
14+
turnkeyStrategy,
15+
address,
16+
broadcaster,
17+
oidcToken,
18+
} from './reactives'
2419
20+
const turnkeyAuthIframeContainerId = 'turnkey-auth-iframe-container-id'
2521
const turnkeyReadyAndLoggedIn = computed(() => {
2622
return turnkeyStatus.value === 'logged-in' && turnkeyStrategy.value
2723
})
2824
2925
onMounted(async () => {
30-
const _turnkeyStrategy = await TurnkeyWallet.create({
26+
const _turnkeyStrategy = new TurnkeyWallet({
3127
onStatusChange(status) {
3228
turnkeyStatus.value = status
3329
},
@@ -46,13 +42,20 @@ onMounted(async () => {
4642
turnkeyStrategy.value = _turnkeyStrategy
4743
})
4844
45+
onMounted(async () => {
46+
const hashParams = new URLSearchParams(window.location.hash.substring(1))
47+
const params = {
48+
idToken: hashParams.get('id_token'),
49+
}
50+
51+
oidcToken.value = params.idToken
52+
})
53+
4954
watch(turnkeyReadyAndLoggedIn, async (_ready) => {
5055
if (!_ready) {
5156
return
5257
}
5358
54-
console.log('Watching and updating broadcaster')
55-
5659
const _walletStrategy = new BaseWalletStrategy({
5760
strategies: {
5861
[Wallet.Turnkey]: turnkeyStrategy.value as TurnkeyWalletStrategy,
@@ -69,50 +72,19 @@ watch(turnkeyReadyAndLoggedIn, async (_ready) => {
6972
})
7073
7174
const addresses = await _walletStrategy?.getAddresses()
72-
console.log('🪵 | addresses:', addresses)
73-
console.log('relevant address: ', addresses[0])
7475
address.value = addresses[0]
7576
})
76-
77-
watch(turnkeyStatus, (status) => {
78-
console.log('🪵 | turnkeyStatus:', status)
79-
})
80-
81-
async function sendTx(msgs: Msgs): Promise<TxResponse> {
82-
if (!broadcaster.value) {
83-
throw new Error('Broadcaster not initialized')
84-
}
85-
86-
if (!address.value) {
87-
throw new Error('Address not initialized')
88-
}
89-
90-
const result = await broadcaster.value.broadcastWithFeeDelegation({
91-
msgs,
92-
injectiveAddress: address.value,
93-
ethereumAddress: getEthereumAddress(address.value),
94-
})
95-
96-
console.log('🪵 | result:', result)
97-
98-
return result
99-
}
10077
</script>
10178

10279
<template>
10380
<h1>Injective + Turnkey</h1>
104-
<LoginForm
105-
:init-email-o-t-p="turnkeyStrategy.initEmailOTP.bind(turnkeyStrategy)"
106-
:confirm-email-o-t-p="turnkeyStrategy.confirmEmailOTP.bind(turnkeyStrategy)"
107-
:turnkey-status="turnkeyStatus"
108-
v-if="turnkeyStrategy && turnkeyStatus && turnkeyStatus !== 'logged-in'"
109-
/>
110-
<Connected
111-
v-if="address && turnkeyStatus === 'logged-in'"
112-
:address="address"
113-
:send-tx="sendTx"
114-
:logout="turnkeyStrategy?.disconnect.bind(turnkeyStrategy)"
115-
/>
81+
<div v-if="turnkeyStatus === 'initializing'">Loading...</div>
82+
<div v-else>
83+
<LoginForm
84+
v-if="turnkeyStrategy && turnkeyStatus && turnkeyStatus !== 'logged-in'"
85+
/>
86+
<Connected v-if="address && turnkeyStatus === 'logged-in'" />
87+
</div>
11688
<div :id="turnkeyAuthIframeContainerId" style="display: none"></div>
11789
</template>
11890

examples/turnkey/app/src/components/Connected.vue

+112-33
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,59 @@
11
<script lang="ts" setup>
2-
import { MsgSend, type Msgs, type TxResponse } from '@injectivelabs/sdk-ts'
3-
import { ref } from 'vue'
4-
5-
const { address, sendTx, logout } = defineProps<{
6-
address: string
7-
sendTx: (msgs: Msgs) => Promise<TxResponse>
8-
logout?: () => Promise<void>
9-
}>()
10-
11-
const to = ref(address)
12-
const from = ref(address)
13-
const amount = ref('1000000')
14-
// const amount = ref('1')
15-
const denom = ref(
16-
'factory/inj1dm0yt646fsjsvznjz2twyht9ytcmwz3aqydjjp/RealTrumPepe',
17-
)
18-
// const denom = ref('inj')
2+
import { getEthereumAddress, MsgSend } from '@injectivelabs/sdk-ts'
3+
import { onMounted, ref } from 'vue'
4+
import { address, turnkeyStrategy, broadcaster, oidcToken } from '../reactives'
5+
import { generateGoogleUrl } from '../utils'
6+
7+
const msgValues = ref({
8+
to: address.value,
9+
from: address.value,
10+
amount: '1000000',
11+
denom: 'factory/inj1dm0yt646fsjsvznjz2twyht9ytcmwz3aqydjjp/RealTrumPepe',
12+
})
1913
2014
const txHash = ref('')
2115
2216
const isLoading = ref(false)
2317
24-
const handleSubmit = async () => {
25-
console.log('to', to.value)
26-
console.log('amount', amount.value)
27-
28-
if (!to.value || !amount.value || !denom.value) {
18+
const handleSendClick = async () => {
19+
if (
20+
!msgValues.value.to ||
21+
!msgValues.value.from ||
22+
!msgValues.value.amount ||
23+
!msgValues.value.denom
24+
) {
2925
throw new Error('Invalid input')
3026
}
3127
28+
if (!address.value) {
29+
throw new Error('Address not initialized')
30+
}
31+
32+
if (!broadcaster.value) {
33+
throw new Error('Broadcaster not initialized')
34+
}
35+
3236
isLoading.value = true
3337
3438
const msgs = MsgSend.fromJSON({
35-
srcInjectiveAddress: from.value,
36-
dstInjectiveAddress: to.value,
39+
srcInjectiveAddress: msgValues.value.from,
40+
dstInjectiveAddress: msgValues.value.to,
3741
amount: [
3842
{
39-
denom: denom.value,
40-
amount: amount.value,
43+
denom: msgValues.value.denom,
44+
amount: msgValues.value.amount,
4145
},
4246
],
4347
})
4448
4549
try {
46-
const result = await sendTx(msgs)
50+
const result = await broadcaster.value.broadcastWithFeeDelegation({
51+
msgs,
52+
injectiveAddress: address.value,
53+
ethereumAddress: getEthereumAddress(address.value),
54+
})
55+
56+
console.log('🪵 | result:', result)
4757
if (result.txHash) {
4858
txHash.value = result.txHash
4959
}
@@ -53,11 +63,70 @@ const handleSubmit = async () => {
5363
isLoading.value = false
5464
}
5565
}
66+
67+
async function deleteOrganization() {
68+
if (!turnkeyStrategy.value?.authIframeClient) {
69+
throw new Error('Auth iframe client not initialized')
70+
}
71+
72+
const result =
73+
await turnkeyStrategy.value.authIframeClient.deleteSubOrganization({
74+
deleteWithoutExport: true,
75+
organizationId: turnkeyStrategy.value.organizationId,
76+
timestampMs: Date.now().toString(),
77+
})
78+
console.log('🪵 | deleteOrganization | result:', result)
79+
}
80+
81+
async function addGoogleOAuth() {
82+
if (!turnkeyStrategy.value?.authIframeClient) {
83+
throw new Error('Auth iframe client not initialized')
84+
}
85+
const nonce = await turnkeyStrategy.value.generateOAuthNonce()
86+
87+
const googleUrl = generateGoogleUrl(nonce)
88+
89+
window.location.href = googleUrl
90+
}
91+
92+
async function updateUserWithToken(oidcToken: string) {
93+
if (!turnkeyStrategy.value?.authIframeClient) {
94+
throw new Error('Auth iframe client not initialized')
95+
}
96+
97+
const user = await turnkeyStrategy.value.turnkey.getCurrentUser()
98+
99+
if (!user) {
100+
throw new Error('User not found')
101+
}
102+
103+
const result =
104+
await turnkeyStrategy.value.authIframeClient.createOauthProviders({
105+
organizationId: turnkeyStrategy.value.organizationId,
106+
userId: user?.userId,
107+
oauthProviders: [{ providerName: 'google', oidcToken }],
108+
})
109+
110+
console.log('🪵 | updateUserWithToken | result:', result)
111+
112+
// Clear all params from URL either way
113+
window.history.replaceState({}, '', window.location.pathname)
114+
}
115+
116+
onMounted(async () => {
117+
if (oidcToken.value) {
118+
updateUserWithToken(oidcToken.value)
119+
}
120+
})
56121
</script>
57122

58123
<template>
59124
<h2>Connected!</h2>
60-
<button @click="logout">Logout</button>
125+
<div class="actions">
126+
<button @click="turnkeyStrategy?.disconnect()">Logout</button>
127+
<button @click="deleteOrganization">Delete Organization (Account)</button>
128+
<button @click="addGoogleOAuth">Add Google OAuth</button>
129+
</div>
61130
<div>
62131
<span>Addresses: {{ address }}</span>
63132
</div>
@@ -66,17 +135,21 @@ const handleSubmit = async () => {
66135
<form>
67136
<label>
68137
<span>To</span>
69-
<input type="text" v-model="to" />
138+
<input type="text" v-model="msgValues.to" />
70139
</label>
71140
<label>
72141
<span>Amount</span>
73-
<input type="text" v-model="amount" />
142+
<input type="text" v-model="msgValues.amount" />
74143
</label>
75144
<label>
76145
<span>Denom</span>
77-
<input type="text" v-model="denom" />
146+
<input type="text" v-model="msgValues.denom" />
78147
</label>
79-
<button type="submit" @click.prevent="handleSubmit" :disabled="isLoading">
148+
<button
149+
type="submit"
150+
@click.prevent="handleSendClick"
151+
:disabled="isLoading"
152+
>
80153
{{ isLoading ? 'Sending...' : 'Submit' }}
81154
</button>
82155
<div v-if="txHash">
@@ -88,7 +161,13 @@ const handleSubmit = async () => {
88161
</form>
89162
</template>
90163

91-
<style>
164+
<style scoped>
165+
.actions {
166+
display: flex;
167+
gap: 10px;
168+
padding-bottom: 10px;
169+
}
170+
92171
form {
93172
display: flex;
94173
flex-direction: column;

0 commit comments

Comments
 (0)