Skip to content

Commit cb040c4

Browse files
authored
refactor: added back support for did/PDS logins and passes the locale onto the oauth screen (#1187)
1 parent 3030bc0 commit cb040c4

File tree

7 files changed

+43
-11
lines changed

7 files changed

+43
-11
lines changed

app/components/Header/AuthModal.client.vue

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
22
import { useAtproto } from '~/composables/atproto/useAtproto'
33
import { authRedirect } from '~/utils/atproto/helpers'
4+
import { ensureValidAtIdentifier } from '@atproto/syntax'
45
56
const handleInput = shallowRef('')
7+
const errorMessage = shallowRef('')
68
const route = useRoute()
79
const { user, logout } = useAtproto()
810
@@ -13,26 +15,41 @@ const localeSubPath = ['ko', 'pt', 'ja'].includes(currentLang) ? currentLang : '
1315
const atprotoLink = `https://atproto.com/${localeSubPath}`
1416
1517
async function handleBlueskySignIn() {
16-
await authRedirect('https://bsky.social', { redirectTo: route.fullPath })
18+
await authRedirect('https://bsky.social', { redirectTo: route.fullPath, locale: locale.value })
1719
}
1820
1921
async function handleCreateAccount() {
20-
await authRedirect('https://npmx.social', { create: true, redirectTo: route.fullPath })
22+
await authRedirect('https://npmx.social', {
23+
create: true,
24+
redirectTo: route.fullPath,
25+
locale: locale.value,
26+
})
2127
}
2228
2329
async function handleLogin() {
2430
if (handleInput.value) {
25-
await authRedirect(handleInput.value)
31+
// URLS to PDSs are valid for oauth redirects
32+
if (!handleInput.value.startsWith('https://')) {
33+
try {
34+
ensureValidAtIdentifier(handleInput.value)
35+
} catch (error) {
36+
errorMessage.value =
37+
error instanceof Error ? error.message : $t('auth.modal.default_input_error')
38+
return
39+
}
40+
}
41+
await authRedirect(handleInput.value, {
42+
redirectTo: route.fullPath,
43+
locale: locale.value,
44+
})
2645
}
2746
}
2847
2948
watch(handleInput, newHandleInput => {
49+
errorMessage.value = ''
3050
if (!newHandleInput) return
3151
32-
const normalized = newHandleInput
33-
.trim()
34-
.toLowerCase()
35-
.replace(/[^a-z0-9.-]/g, '')
52+
const normalized = newHandleInput.trim().toLowerCase().replace(/@/g, '')
3653
3754
if (normalized !== newHandleInput) {
3855
handleInput.value = normalized
@@ -81,6 +98,9 @@ watch(handleInput, newHandleInput => {
8198
v-bind="noCorrect"
8299
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 hover:border-fg-subtle focus:border-accent focus-visible:(outline-2 outline-accent/70)"
83100
/>
101+
<p v-if="errorMessage" class="text-red-500 text-xs mt-1" role="alert">
102+
{{ errorMessage }}
103+
</p>
84104
</div>
85105

86106
<details class="text-sm">

app/utils/atproto/helpers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import type { LocationQueryRaw } from 'vue-router'
44
interface AuthRedirectOptions {
55
create?: boolean
66
redirectTo?: string
7+
locale?: string
78
}
89

910
/**
1011
* Redirect user to ATProto authentication
1112
*/
1213
export async function authRedirect(identifier: string, options: AuthRedirectOptions = {}) {
13-
let query: LocationQueryRaw = { handle: identifier }
14+
let query: LocationQueryRaw = { handle: identifier, locale: options.locale || 'en' }
1415
if (options.create) {
1516
query = { ...query, create: 'true' }
1617
}

i18n/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,8 @@
796796
"create_account": "Create a new account",
797797
"connect_bluesky": "Connect with Bluesky",
798798
"what_is_atmosphere": "What is an Atmosphere account?",
799-
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account."
799+
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account.",
800+
"default_input_error": "Please enter a valid handle, DID, or a full PDS URL"
800801
}
801802
},
802803
"header": {

lunaria/files/en-GB.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,8 @@
796796
"create_account": "Create a new account",
797797
"connect_bluesky": "Connect with Bluesky",
798798
"what_is_atmosphere": "What is an Atmosphere account?",
799-
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account."
799+
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account.",
800+
"default_input_error": "Please enter a valid handle, DID, or a full PDS URL"
800801
}
801802
},
802803
"header": {

lunaria/files/en-US.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,8 @@
796796
"create_account": "Create a new account",
797797
"connect_bluesky": "Connect with Bluesky",
798798
"what_is_atmosphere": "What is an Atmosphere account?",
799-
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account."
799+
"atmosphere_explanation": "{npmx} uses the {atproto} to power many of its social features, allowing users to own their data and use one account for all compatible applications. Once you create an account, you can use other apps like {bluesky} and {tangled} with the same account.",
800+
"default_input_error": "Please enter a valid handle, DID, or a full PDS URL"
800801
}
801802
},
802803
"header": {

nuxt.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ export default defineNuxtConfig({
262262
'semver',
263263
'validate-npm-package-name',
264264
'@atproto/lex',
265+
'@atproto/syntax',
265266
'fast-npm-meta',
266267
'@floating-ui/vue',
267268
],

server/api/auth/atproto.get.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import { ensureValidAtIdentifier } from '@atproto/syntax'
1313
// @ts-expect-error virtual file from oauth module
1414
import { clientUri } from '#oauth/config'
1515

16+
//I did not have luck with other ones than these. I got this list from the PDS language picker
17+
const OAUTH_LOCALES = new Set(['en', 'fr-FR', 'ja-JP'])
18+
1619
/**
1720
* Fetch the user's profile record to get their avatar blob reference
1821
* @param did
@@ -98,9 +101,13 @@ export default defineEventHandler(async event => {
98101
})
99102
}
100103

104+
const localeFromQuery = query.locale?.toString() ?? 'en'
105+
const locale = OAUTH_LOCALES.has(localeFromQuery) ? localeFromQuery : 'en'
106+
101107
const redirectUrl = await atclient.authorize(handle, {
102108
scope,
103109
prompt: create ? 'create' : undefined,
110+
ui_locales: locale,
104111
})
105112
return sendRedirect(event, redirectUrl.toString())
106113
} catch (error) {

0 commit comments

Comments
 (0)