nuxt-atproto 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +271 -0
- package/dist/module.d.mts +56 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +76 -0
- package/dist/runtime/composables/useAtproto.d.ts +0 -0
- package/dist/runtime/composables/useAtproto.js +74 -0
- package/dist/runtime/plugin.client.d.ts +12 -0
- package/dist/runtime/plugin.client.js +61 -0
- package/dist/types.d.mts +7 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 dxlliv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
19
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
20
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# nuxt-atproto
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![License][license-src]][license-href]
|
|
6
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
|
+
|
|
8
|
+
> Easily integrate [Bluesky](https://bsky.app) login and [AT Protocol](https://atproto.com/) authentication into your
|
|
9
|
+
Nuxt.js app.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
`nuxt-atproto` is a Nuxt.js module that simplifies the OAuth authentication via AT Protocol.
|
|
14
|
+
|
|
15
|
+
It handles the login and the session management using the `@atproto/oauth-client` library,
|
|
16
|
+
providing public and authenticated agents for seamless interaction with AT Protocol services.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- SSR-friendly login via Bluesky and AT Protocol with automatic service endpoint resolution.
|
|
21
|
+
- Allow logins without specifying the handle, enabling account selection from PDS interface.
|
|
22
|
+
- Simple access to sign-in, sign-out, public and private agents from `useAtproto` composable.
|
|
23
|
+
- Access to the underlying client and session with `$atproto` provided by the plugin.
|
|
24
|
+
- Dynamically generates `client-metadata.json` when the Nuxt.js app starts.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Install the module via npm:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install nuxt-atproto
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
Register the module in your `nuxt.config.js`:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export default defineNuxtConfig({
|
|
40
|
+
modules: ['nuxt-atproto']
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You can configure `nuxt-atproto` in your `nuxt.config.ts` file under the `atproto` key.
|
|
45
|
+
The following options are available with their default values:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
defineNuxtConfig({
|
|
49
|
+
modules: ['nuxt-atproto'],
|
|
50
|
+
atproto: {
|
|
51
|
+
serviceEndpoint: {
|
|
52
|
+
private: 'https://bsky.social',
|
|
53
|
+
public: 'https://public.api.bsky.app'
|
|
54
|
+
},
|
|
55
|
+
oauth: {
|
|
56
|
+
clientMetadata: {
|
|
57
|
+
// url of your remote client_metadata.json, leave the field empty
|
|
58
|
+
// to let `nuxt-atproto` generate a local /public/client_metadata.json
|
|
59
|
+
remote: '',
|
|
60
|
+
// configuration for the local client_metadata.json
|
|
61
|
+
local: {
|
|
62
|
+
client_id: '',
|
|
63
|
+
client_name: '',
|
|
64
|
+
client_uri: '',
|
|
65
|
+
logo_uri: '',
|
|
66
|
+
tos_uri: '',
|
|
67
|
+
policy_uri: '',
|
|
68
|
+
redirect_uris: [''],
|
|
69
|
+
scope: 'atproto',
|
|
70
|
+
grant_types: [],
|
|
71
|
+
response_types: [],
|
|
72
|
+
token_endpoint_auth_method: 'none',
|
|
73
|
+
application_type: 'web',
|
|
74
|
+
dpop_bound_access_tokens: true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
signInOptions: {
|
|
78
|
+
state: '',
|
|
79
|
+
prompt: 'login',
|
|
80
|
+
scope: 'atproto',
|
|
81
|
+
ui_locales: 'en',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
debug: true,
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
You must configure the `atproto.oauth.clientMetadata` with in your `nuxt.config.ts`,
|
|
90
|
+
especially `client_id` and `redirect_uris`, for the authentication flow to work correctly.
|
|
91
|
+
|
|
92
|
+
If you don't provide a remote URL in the module options, when Nuxt.js starts it will create a `client-metadata.json` in your `public` folder.
|
|
93
|
+
Using a local `client-metadata.json` generally offers a faster user experience compared to fetching it from a remote URL.
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
#### Basic example
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<script setup lang="ts">
|
|
101
|
+
const atproto = useAtproto()
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<div>
|
|
106
|
+
<Button @click="atproto.signIn()">
|
|
107
|
+
Sign-in with ATProto
|
|
108
|
+
</Button><br />
|
|
109
|
+
<Button @click="atproto.signInWithHandle('dxlliv.bsky.social')">
|
|
110
|
+
Sign-in with ATProto using dxlliv.bsky.social
|
|
111
|
+
</Button><br />
|
|
112
|
+
<Button @click="atproto.signInWithHandle()">
|
|
113
|
+
Sign-in with ATProto using your handle (prompt)
|
|
114
|
+
</Button>
|
|
115
|
+
<Button @click="atproto.restore('did:plc:2pkidgvfnbxx7sq3shporxij')">
|
|
116
|
+
Restore dxlliv.bsky.social session
|
|
117
|
+
</Button>
|
|
118
|
+
</div>
|
|
119
|
+
<template v-if="atproto.agent.account">
|
|
120
|
+
<div>logged with: {{atproto.agent.account.assertDid}}</div>
|
|
121
|
+
<Button @click="atproto.signOut()">
|
|
122
|
+
Sign-out
|
|
123
|
+
</Button>
|
|
124
|
+
</template>
|
|
125
|
+
</template>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
<br />
|
|
129
|
+
|
|
130
|
+
## 🧩 useAtproto(service?: string, fetch?: any)
|
|
131
|
+
|
|
132
|
+
A composable provided by `nuxt-atproto` that offers methods for user authentication and session management, including authenticating, signing out and session restore.
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
|
|
136
|
+
<script setup lang="ts">
|
|
137
|
+
const atproto = useAtproto()
|
|
138
|
+
</script>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
|
|
143
|
+
- `service` (optional): Override the service endpoint of the public agent.
|
|
144
|
+
- `fetch` (optional): A custom fetch implementation.
|
|
145
|
+
|
|
146
|
+
<br />
|
|
147
|
+
|
|
148
|
+
### ➡️ signIn(serviceEndpoint?: string, options?: AtprotoSignInOptions)
|
|
149
|
+
|
|
150
|
+
Initiates the standard ATProto sign-in flow redirecting the user for authentication.
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
|
|
154
|
+
<Button @click="atproto.signIn()">
|
|
155
|
+
Sign-in with ATProto
|
|
156
|
+
</Button>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Parameters:**
|
|
160
|
+
|
|
161
|
+
- `serviceEndpoint`: *(optional)* The specific ATProto service endpoint to use for sign-in.
|
|
162
|
+
- `options`: *(optional)* Additional options to configure the sign-in process.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
// options
|
|
166
|
+
{
|
|
167
|
+
state: "",
|
|
168
|
+
prompt: "login",
|
|
169
|
+
scope: "atproto",
|
|
170
|
+
ui_locales: "en"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Returns:** A Promise that might not directly resolve due to the redirection to the ATProto service.
|
|
175
|
+
|
|
176
|
+
<br />
|
|
177
|
+
|
|
178
|
+
### ➡️ signInWithHandle(handle?: string, options?: AtprotoSignInOptions)
|
|
179
|
+
|
|
180
|
+
Initiates the ATProto sign-in flow using the user's AT Protocol handle.
|
|
181
|
+
The user will be prompted to enter their handle on the ATProto service if you omit the handle.
|
|
182
|
+
|
|
183
|
+
```html
|
|
184
|
+
|
|
185
|
+
<Button @click="atproto.signInWithHandle()">
|
|
186
|
+
Sign-in with ATProto
|
|
187
|
+
</Button>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Parameters:**
|
|
191
|
+
|
|
192
|
+
- `handle`: *(optional)* The AT Protocol handle of the user.
|
|
193
|
+
- `options`: *(optional)* Additional options to configure the sign-in process.
|
|
194
|
+
|
|
195
|
+
**Returns:** A Promise that might not directly resolve due to the redirection to the ATProto service.
|
|
196
|
+
|
|
197
|
+
<br />
|
|
198
|
+
|
|
199
|
+
### ➡️ restore(did: string)
|
|
200
|
+
|
|
201
|
+
Restore the user account associated with the provided Decentralized Identifier (DID).
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
|
|
205
|
+
<Button @click="atproto.restore('did:plc:2pkidgvfnbxx7sq3shporxij')">
|
|
206
|
+
Restore dxlliv.bsky.social
|
|
207
|
+
</Button>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Parameters:**
|
|
211
|
+
|
|
212
|
+
- `handle`: *(optional)* The Decentralized Identifier (DID) of the account to be restored.
|
|
213
|
+
|
|
214
|
+
**Returns:** A Promise that resolves when the restoration process is complete.
|
|
215
|
+
|
|
216
|
+
<br />
|
|
217
|
+
|
|
218
|
+
### ➡️ signOut()
|
|
219
|
+
|
|
220
|
+
Logs out the currently authenticated user and clears the stored session data.
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
|
|
224
|
+
<Button @click="atproto.signOut()">
|
|
225
|
+
Sign-out
|
|
226
|
+
</Button>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Returns:** A Promise that resolves when the restoration process is complete.
|
|
230
|
+
|
|
231
|
+
<br />
|
|
232
|
+
|
|
233
|
+
### 🔢 agent
|
|
234
|
+
|
|
235
|
+
Provides an object containing both a public `AtpAgent` for general access and an authenticated `Agent` to perform actions that require a logged-in user, such as posting, following, and liking.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
const atproto = useAtproto()
|
|
239
|
+
|
|
240
|
+
const profile = await atproto.agent.public.getProfile({actor: 'owdproject.org'})
|
|
241
|
+
console.log(profile.data)
|
|
242
|
+
|
|
243
|
+
const likedPost = await atproto.agent.account.likePost({cid, uri})
|
|
244
|
+
console.log(likedPost.data)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Parameters:**
|
|
248
|
+
|
|
249
|
+
- `service` (optional): The base URL of the ATProto service.
|
|
250
|
+
- `fetch` (optional): A custom fetch implementation.
|
|
251
|
+
|
|
252
|
+
**Returns:** An object with both public AtpAgent or authenticated account Agent.
|
|
253
|
+
|
|
254
|
+
<br />
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
This package is released under the MIT license.
|
|
259
|
+
|
|
260
|
+
<!-- Badges -->
|
|
261
|
+
[npm-version-src]: https://img.shields.io/npm/v/nuxt-atproto/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
262
|
+
[npm-version-href]: https://npmjs.com/package/nuxt-atproto
|
|
263
|
+
|
|
264
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-atproto.svg?style=flat&colorA=020420&colorB=00DC82
|
|
265
|
+
[npm-downloads-href]: https://npm.chart.dev/nuxt-atproto
|
|
266
|
+
|
|
267
|
+
[license-src]: https://img.shields.io/npm/l/nuxt-atproto.svg?style=flat&colorA=020420&colorB=00DC82
|
|
268
|
+
[license-href]: https://npmjs.com/package/nuxt-atproto
|
|
269
|
+
|
|
270
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
271
|
+
[nuxt-href]: https://nuxt.com
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { HookResult } from '@nuxt/schema';
|
|
3
|
+
|
|
4
|
+
declare module '#app' {
|
|
5
|
+
interface RuntimeNuxtHooks {
|
|
6
|
+
'atproto:sessionCreated': (did: string) => HookResult
|
|
7
|
+
'atproto:sessionRestored': (did: string) => HookResult
|
|
8
|
+
'atproto:sessionDeleted': (did: string) => HookResult
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare module 'nuxt/schema' {
|
|
13
|
+
interface PublicRuntimeConfig {
|
|
14
|
+
atproto: AtprotoNuxtOptions
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface AtprotoNuxtOptions {
|
|
19
|
+
serviceEndpoint: {
|
|
20
|
+
private: string
|
|
21
|
+
public: string
|
|
22
|
+
}
|
|
23
|
+
oauth: {
|
|
24
|
+
clientMetadata: {
|
|
25
|
+
remote?: string
|
|
26
|
+
local?: {
|
|
27
|
+
client_id: string
|
|
28
|
+
client_name: string
|
|
29
|
+
client_uri: string
|
|
30
|
+
logo_uri: string
|
|
31
|
+
tos_uri: string
|
|
32
|
+
policy_uri: string
|
|
33
|
+
redirect_uris: [string, ...string[]]
|
|
34
|
+
scope: string
|
|
35
|
+
grant_types: Array<any>
|
|
36
|
+
response_types: Array<any>
|
|
37
|
+
token_endpoint_auth_method: string
|
|
38
|
+
application_type: string
|
|
39
|
+
dpop_bound_access_tokens: boolean
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
signInOptions: AtprotoSignInOptions
|
|
43
|
+
}
|
|
44
|
+
debug: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface AtprotoSignInOptions {
|
|
48
|
+
state: string
|
|
49
|
+
prompt: 'login' | 'consent'
|
|
50
|
+
scope: string
|
|
51
|
+
ui_locales: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare const _default: _nuxt_schema.NuxtModule<AtprotoNuxtOptions, AtprotoNuxtOptions, false>;
|
|
55
|
+
|
|
56
|
+
export { _default as default };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { defineNuxtModule, createResolver, addImportsDir, addPlugin } from '@nuxt/kit';
|
|
3
|
+
|
|
4
|
+
const module = defineNuxtModule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "nuxt-atproto",
|
|
7
|
+
configKey: "atproto"
|
|
8
|
+
},
|
|
9
|
+
defaults: {
|
|
10
|
+
serviceEndpoint: {
|
|
11
|
+
private: "https://bsky.social",
|
|
12
|
+
public: "https://public.api.bsky.app"
|
|
13
|
+
},
|
|
14
|
+
oauth: {
|
|
15
|
+
clientMetadata: {
|
|
16
|
+
// load the client_metadata.json asynchronously from the URL
|
|
17
|
+
remote: "",
|
|
18
|
+
// or use the local client_metadata.json generated by `nuxt-atproto`
|
|
19
|
+
local: {
|
|
20
|
+
client_id: "",
|
|
21
|
+
client_name: "",
|
|
22
|
+
client_uri: "",
|
|
23
|
+
logo_uri: "",
|
|
24
|
+
tos_uri: "",
|
|
25
|
+
policy_uri: "",
|
|
26
|
+
redirect_uris: [""],
|
|
27
|
+
scope: "atproto",
|
|
28
|
+
grant_types: [],
|
|
29
|
+
response_types: [],
|
|
30
|
+
token_endpoint_auth_method: "none",
|
|
31
|
+
application_type: "web",
|
|
32
|
+
dpop_bound_access_tokens: true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
signInOptions: {
|
|
36
|
+
state: "",
|
|
37
|
+
prompt: "login",
|
|
38
|
+
scope: "atproto",
|
|
39
|
+
ui_locales: "en"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
debug: true
|
|
43
|
+
},
|
|
44
|
+
setup(_options, _nuxt) {
|
|
45
|
+
const { resolve } = createResolver(import.meta.url);
|
|
46
|
+
const publicDir = resolve(_nuxt.options.rootDir, "public");
|
|
47
|
+
_nuxt.options.runtimeConfig.public.atproto = _options;
|
|
48
|
+
_nuxt.options.build.transpile = _nuxt.options.build.transpile || [];
|
|
49
|
+
_nuxt.options.build.transpile.push("@atproto/oauth-client-browser");
|
|
50
|
+
_nuxt.options.build.transpile.push("@atproto/api");
|
|
51
|
+
_nuxt.hook("vite:extendConfig", (config) => {
|
|
52
|
+
config.optimizeDeps = config.optimizeDeps || {};
|
|
53
|
+
config.optimizeDeps.include = config.optimizeDeps.include || [];
|
|
54
|
+
config.optimizeDeps.include.push("@atproto/oauth-client-browser");
|
|
55
|
+
config.optimizeDeps.include.push("@atproto/api");
|
|
56
|
+
});
|
|
57
|
+
if (!_options.oauth.clientMetadata.remote) {
|
|
58
|
+
try {
|
|
59
|
+
mkdirSync(publicDir, { recursive: true });
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("Failed creating /public/client-metadata.json", error);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
writeFileSync(
|
|
65
|
+
publicDir + "/client-metadata.json",
|
|
66
|
+
JSON.stringify(_options.oauth.clientMetadata.local, null, 2)
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
rmSync(publicDir + "/client-metadata.json");
|
|
70
|
+
}
|
|
71
|
+
addImportsDir(resolve("./runtime/composables"));
|
|
72
|
+
addPlugin(resolve("./runtime/plugin.client"));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export { module as default };
|
|
File without changes
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Agent, AtpAgent } from "@atproto/api";
|
|
2
|
+
import { useRuntimeConfig, useNuxtApp } from "nuxt/app";
|
|
3
|
+
import { reactive } from "vue";
|
|
4
|
+
export function useAtproto(service, fetch) {
|
|
5
|
+
const runtimeConfig = useRuntimeConfig();
|
|
6
|
+
async function signIn(serviceEndpoint = runtimeConfig.public.atproto.serviceEndpoint.private, options = runtimeConfig.public.atproto.oauth.signInOptions) {
|
|
7
|
+
const { $atproto } = useNuxtApp();
|
|
8
|
+
try {
|
|
9
|
+
await $atproto.client.signInRedirect(
|
|
10
|
+
serviceEndpoint,
|
|
11
|
+
{
|
|
12
|
+
...options,
|
|
13
|
+
signal: new AbortController().signal
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error(`Error during sign-in:`, error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function signInWithHandle(handle, options = runtimeConfig.public.atproto.oauth.signInOptions) {
|
|
21
|
+
const { $atproto } = useNuxtApp();
|
|
22
|
+
try {
|
|
23
|
+
let handlePrompt = null;
|
|
24
|
+
if (!handle) {
|
|
25
|
+
handlePrompt = window.prompt("Type your handle");
|
|
26
|
+
if (!handlePrompt) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
handle = handlePrompt;
|
|
30
|
+
}
|
|
31
|
+
const url = await $atproto.client.authorize(handle, options);
|
|
32
|
+
window.location.href = url.href;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error during sign-in for handle ${handle}:`, error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function restore(did) {
|
|
38
|
+
const { $atproto } = useNuxtApp();
|
|
39
|
+
const session = await $atproto.client.restore(did);
|
|
40
|
+
$atproto.session.value = session;
|
|
41
|
+
return session;
|
|
42
|
+
}
|
|
43
|
+
async function signOut() {
|
|
44
|
+
const { $atproto } = useNuxtApp();
|
|
45
|
+
$atproto.client.revoke($atproto.session.value.did);
|
|
46
|
+
$atproto.session.value = void 0;
|
|
47
|
+
if (runtimeConfig.public.atproto.debug) {
|
|
48
|
+
console.log("User signed out");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function useAgent(service2, fetch2) {
|
|
52
|
+
const { $atproto } = useNuxtApp();
|
|
53
|
+
const runtimeConfig2 = useRuntimeConfig();
|
|
54
|
+
if (!service2) {
|
|
55
|
+
service2 = runtimeConfig2.public.atproto.serviceEndpoint.public;
|
|
56
|
+
}
|
|
57
|
+
let accountAgent;
|
|
58
|
+
if ($atproto && $atproto.session.value) {
|
|
59
|
+
accountAgent = new Agent($atproto.session.value);
|
|
60
|
+
}
|
|
61
|
+
return reactive({
|
|
62
|
+
public: new AtpAgent({ service: service2, fetch: fetch2 }),
|
|
63
|
+
account: accountAgent
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const agent = useAgent(service, fetch);
|
|
67
|
+
return {
|
|
68
|
+
signIn,
|
|
69
|
+
signInWithHandle,
|
|
70
|
+
signOut,
|
|
71
|
+
restore,
|
|
72
|
+
agent
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const _default: import("nuxt/app").Plugin<{
|
|
2
|
+
atproto: {
|
|
3
|
+
client: any;
|
|
4
|
+
session: import("vue").Ref<any, any>;
|
|
5
|
+
};
|
|
6
|
+
}> & import("nuxt/app").ObjectPlugin<{
|
|
7
|
+
atproto: {
|
|
8
|
+
client: any;
|
|
9
|
+
session: import("vue").Ref<any, any>;
|
|
10
|
+
};
|
|
11
|
+
}>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BrowserOAuthClient } from "@atproto/oauth-client-browser";
|
|
2
|
+
import { useRuntimeConfig, defineNuxtPlugin } from "nuxt/app";
|
|
3
|
+
import { ref } from "vue";
|
|
4
|
+
export default defineNuxtPlugin({
|
|
5
|
+
name: "atproto",
|
|
6
|
+
async setup(_nuxtApp) {
|
|
7
|
+
const runtimeConfig = useRuntimeConfig();
|
|
8
|
+
let atprotoOAuthClient;
|
|
9
|
+
const atprotoOAuthSession = ref();
|
|
10
|
+
if (runtimeConfig.public.atproto.oauth.clientMetadata.remote) {
|
|
11
|
+
atprotoOAuthClient = await BrowserOAuthClient.load({
|
|
12
|
+
handleResolver: runtimeConfig.public.atproto.serviceEndpoint.private,
|
|
13
|
+
clientId: runtimeConfig.public.atproto.oauth.clientMetadata.remote
|
|
14
|
+
// todo implement custom fetch
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
atprotoOAuthClient = new BrowserOAuthClient({
|
|
18
|
+
handleResolver: runtimeConfig.public.atproto.serviceEndpoint.private,
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
clientMetadata: import.meta.env.MODE === "development" ? void 0 : runtimeConfig.public.atproto.oauth.clientMetadata.local
|
|
21
|
+
// todo implement custom fetch
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
atprotoOAuthClient.addEventListener("deleted", (event) => {
|
|
25
|
+
const { sub, cause } = event.detail;
|
|
26
|
+
if (runtimeConfig.public.atproto.debug) {
|
|
27
|
+
console.warn(`Session deleted for ${sub}. Cause:`, cause);
|
|
28
|
+
}
|
|
29
|
+
_nuxtApp.hooks.callHook("atproto:sessionDeleted", sub);
|
|
30
|
+
});
|
|
31
|
+
await atprotoOAuthClient.init().then(async (result) => {
|
|
32
|
+
if (result) {
|
|
33
|
+
const { session, state } = result;
|
|
34
|
+
if (runtimeConfig.public.atproto.debug) {
|
|
35
|
+
if (state != null) {
|
|
36
|
+
console.log(`${session.sub} was successfully authenticated (state: ${state})`);
|
|
37
|
+
_nuxtApp.hook("app:mounted", async () => {
|
|
38
|
+
_nuxtApp.hooks.callHook("atproto:sessionCreated", session.sub);
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
console.log(`${session.sub} was restored (last active session)`);
|
|
42
|
+
_nuxtApp.hook("app:mounted", async () => {
|
|
43
|
+
_nuxtApp.hooks.callHook("atproto:sessionRestored", session.sub);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
atprotoOAuthSession.value = session;
|
|
48
|
+
}
|
|
49
|
+
}).catch((error) => {
|
|
50
|
+
console.error("Error initializing ATProto client or restoring session:", error);
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
provide: {
|
|
54
|
+
atproto: {
|
|
55
|
+
client: atprotoOAuthClient,
|
|
56
|
+
session: atprotoOAuthSession
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuxt-atproto",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Interact with AT Protocol and Bluesky in your Nuxt.js application",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nuxt",
|
|
7
|
+
"bluesky",
|
|
8
|
+
"atprotocol",
|
|
9
|
+
"oauth",
|
|
10
|
+
"atproto",
|
|
11
|
+
"authentication"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": "nuxt-atproto",
|
|
15
|
+
"homepage": "https://github.com/dxlliv/nuxt-atproto",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "dxlliv",
|
|
18
|
+
"url": "https://github.com/dxlliv"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/types.d.mts",
|
|
24
|
+
"import": "./dist/module.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/module.mjs",
|
|
28
|
+
"typesVersions": {
|
|
29
|
+
"*": {
|
|
30
|
+
".": [
|
|
31
|
+
"./dist/types.d.mts"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"prepack": "nuxt-module-build build",
|
|
40
|
+
"dev": "nuxi dev playground",
|
|
41
|
+
"dev:build": "nuxi build playground",
|
|
42
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
43
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
44
|
+
"lint": "eslint .",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest watch",
|
|
47
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@nuxt/kit": "^3.17.2",
|
|
51
|
+
"@atproto/api": "^0.15.3",
|
|
52
|
+
"@atproto/oauth-client-browser": "^0.3.11"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@nuxt/devtools": "^2.4.0",
|
|
56
|
+
"@nuxt/eslint-config": "^1.3.0",
|
|
57
|
+
"@nuxt/module-builder": "^1.0.1",
|
|
58
|
+
"@nuxt/schema": "^3.17.2",
|
|
59
|
+
"@nuxt/test-utils": "^3.18.0",
|
|
60
|
+
"@types/node": "latest",
|
|
61
|
+
"changelogen": "^0.6.1",
|
|
62
|
+
"eslint": "^9.26.0",
|
|
63
|
+
"nuxt": "^3.17.2",
|
|
64
|
+
"typescript": "~5.8.3",
|
|
65
|
+
"vitest": "^3.1.2",
|
|
66
|
+
"vue-tsc": "^2.2.10"
|
|
67
|
+
},
|
|
68
|
+
"unbuild": {
|
|
69
|
+
"failOnWarn": false
|
|
70
|
+
}
|
|
71
|
+
}
|