Compare commits
2 commits
c323875ec9
...
54c1c990e1
| Author | SHA1 | Date | |
|---|---|---|---|
| 54c1c990e1 | |||
| 7497e6b3e5 |
2 changed files with 29 additions and 9 deletions
|
|
@ -49,7 +49,8 @@ export async function createAppInstance() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Chat has no public view — every non-login route requires auth.
|
// Chat has no public view — every non-login route requires auth.
|
||||||
installStrictAuthGuard(router)
|
// Chat is Nostr end-to-end — it cannot work without an identity.
|
||||||
|
installStrictAuthGuard(router, { requirePubkey: true })
|
||||||
|
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ import type { Router, RouteRecordRaw } from 'vue-router'
|
||||||
* mismatch: guards register early but await this promise before reading
|
* mismatch: guards register early but await this promise before reading
|
||||||
* auth state. Phase 3 calls markAuthReady() once auth is initialized.
|
* auth state. Phase 3 calls markAuthReady() once auth is initialized.
|
||||||
*/
|
*/
|
||||||
type AuthUserLike = { value: { pubkey?: string } | null }
|
type AuthUserLike = { value: { id?: string; pubkey?: string } | null }
|
||||||
type AuthLike = {
|
type AuthLike = {
|
||||||
isAuthenticated: { value: boolean }
|
isAuthenticated: { value: boolean }
|
||||||
// Populated after server-validated getCurrentUser() in auth.checkAuth().
|
// Populated after server-validated getCurrentUser() in auth.checkAuth().
|
||||||
// Guards require BOTH isAuthenticated and a user with a pubkey — token
|
// Guards require BOTH isAuthenticated and a server-populated user —
|
||||||
// presence alone is not enough (issue #36).
|
// token presence alone is not enough (issue #36).
|
||||||
currentUser: AuthUserLike
|
currentUser: AuthUserLike
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,20 +33,39 @@ export function markAuthReady(auth: AuthLike): void {
|
||||||
/**
|
/**
|
||||||
* Belt-and-suspenders auth check: token presence in localStorage isn't
|
* Belt-and-suspenders auth check: token presence in localStorage isn't
|
||||||
* sufficient — the server must have confirmed the token represents a real
|
* sufficient — the server must have confirmed the token represents a real
|
||||||
* session, which is signalled by currentUser being populated with a pubkey.
|
* session, which is signalled by currentUser being populated.
|
||||||
|
*
|
||||||
|
* Keyed on `id`, never `pubkey`. Every account has an id; a Nostr pubkey
|
||||||
|
* is optional — Lightning-only accounts have none. Using pubkey as the
|
||||||
|
* "server answered" marker locked those accounts out of every
|
||||||
|
* strict-guard app: login succeeded, isAuthenticated flipped true, pubkey
|
||||||
|
* stayed empty, isFullyAuthed returned false, and the guard bounced them
|
||||||
|
* back to /login indefinitely. The #36 protection is unchanged — a bare
|
||||||
|
* token in localStorage still does not satisfy this.
|
||||||
*/
|
*/
|
||||||
function isFullyAuthed(auth: AuthLike): boolean {
|
function isFullyAuthed(auth: AuthLike): boolean {
|
||||||
return auth.isAuthenticated.value && !!auth.currentUser?.value?.pubkey
|
return auth.isAuthenticated.value && !!auth.currentUser?.value?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strict guard — every non-/login route requires auth.
|
* Strict guard — every non-/login route requires auth.
|
||||||
* Used by wallet, chat, libra (no public view).
|
* Used by wallet, chat, accounting (no public view).
|
||||||
|
*
|
||||||
|
* `requirePubkey` additionally demands a Nostr identity. Only chat sets
|
||||||
|
* it: chat is Nostr end-to-end and cannot function without a key. Wallet
|
||||||
|
* and accounting are payment/ledger surfaces and must stay reachable
|
||||||
|
* without one — per ADR-0001, LNbits is a liquidity service, not an
|
||||||
|
* identity provider, so core payment flows cannot be gated on identity.
|
||||||
*/
|
*/
|
||||||
export function installStrictAuthGuard(router: Router): void {
|
export function installStrictAuthGuard(
|
||||||
|
router: Router,
|
||||||
|
opts: { requirePubkey?: boolean } = {},
|
||||||
|
): void {
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach(async (to) => {
|
||||||
const auth = await authReady
|
const auth = await authReady
|
||||||
const authed = isFullyAuthed(auth)
|
const authed =
|
||||||
|
isFullyAuthed(auth) &&
|
||||||
|
(!opts.requirePubkey || !!auth.currentUser?.value?.pubkey)
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
return authed ? '/' : true
|
return authed ? '/' : true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue