diff --git a/src/App.tsx b/src/App.tsx index 9f58f21..11434af 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,7 +33,7 @@ const App = () => { window.location.href.split(`${window.location.origin}/#`)[1] ) - return `${appPublicRoutes.login}?callbackPath=${callbackPathEncoded}` + return `${appPublicRoutes.landingPage}?callbackPath=${callbackPathEncoded}` } // Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true diff --git a/src/controllers/NostrController.ts b/src/controllers/NostrController.ts index c3d2908..8e24e91 100644 --- a/src/controllers/NostrController.ts +++ b/src/controllers/NostrController.ts @@ -1,9 +1,12 @@ import { EventTemplate, UnsignedEvent } from 'nostr-tools' -import { WindowNostr } from 'nostr-tools/nip07' import { EventEmitter } from 'tseep' import store from '../store/store' import { SignedEvent } from '../types' import { LoginMethodContext } from '../services/LoginMethodStrategy/loginMethodContext' +import { clear, unixNow } from '../utils' +import { LoginMethod } from '../store/auth/types' +import { logout as nostrLogout } from 'nostr-login' +import { userLogOutAction } from '../store/actions' export class NostrController extends EventEmitter { private static instance: NostrController @@ -11,13 +14,6 @@ export class NostrController extends EventEmitter { private constructor() { super() } - private getNostrObject = () => { - if (window.nostr) return window.nostr as WindowNostr - - throw new Error( - `window.nostr object not present. Make sure you have an nostr extension installed/working properly.` - ) - } public static getInstance(): NostrController { if (!NostrController.instance) { @@ -72,7 +68,22 @@ export class NostrController extends EventEmitter { const loginMethod = store.getState().auth.loginMethod const context = new LoginMethodContext(loginMethod) - return await context.signEvent(event) + const authkey = store.getState().auth.usersPubkey + const signedEvent = await context.signEvent(event) + const pubkey = signedEvent.pubkey + + // Forcefully log out the user if we detect missmatch between pubkeys + // Allow undefined authkey, intial log in + if (authkey && authkey !== pubkey) { + if (loginMethod === LoginMethod.nostrLogin) { + nostrLogout() + } + store.dispatch(userLogOutAction()) + clear() + throw new Error('User missmatch.\n\nPlease log in again.') + } + + return signedEvent } nip04Encrypt = async (receiver: string, content: string): Promise => { @@ -97,23 +108,37 @@ export class NostrController extends EventEmitter { } /** - * Function will capture the public key from the nostr extension or if no extension present - * function wil capture the public key from the local storage + * Function will capture the public key from signedEvent */ capturePublicKey = async (): Promise => { - const nostr = this.getNostrObject() - const pubKey = await nostr.getPublicKey().catch((err: unknown) => { - if (err instanceof Error) { - return Promise.reject(err.message) - } else { - return Promise.reject(JSON.stringify(err)) + try { + const timestamp = unixNow() + const { href } = window.location + + const authEvent: EventTemplate = { + kind: 27235, + tags: [ + ['u', href], + ['method', 'GET'] + ], + content: '', + created_at: timestamp } - }) - if (!pubKey) { - return Promise.reject('Error getting public key, user canceled') + const signedAuthEvent = await this.signEvent(authEvent) + const pubkey = signedAuthEvent.pubkey + + if (!pubkey) { + return Promise.reject('Error getting public key, user canceled') + } + + return Promise.resolve(pubkey) + } catch (error) { + if (error instanceof Error) { + return Promise.reject(error.message) + } else { + return Promise.reject(JSON.stringify(error)) + } } - - return Promise.resolve(pubKey) } } diff --git a/src/layouts/Main.tsx b/src/layouts/Main.tsx index 3e6be26..5410dec 100644 --- a/src/layouts/Main.tsx +++ b/src/layouts/Main.tsx @@ -58,12 +58,12 @@ export const MainLayout = () => { } const login = useCallback(async () => { + dispatch(updateLoginMethod(LoginMethod.nostrLogin)) + const nostrController = NostrController.getInstance() const authController = new AuthController() const pubkey = await nostrController.capturePublicKey() - dispatch(updateLoginMethod(LoginMethod.nostrLogin)) - const redirectPath = await authController.authAndGetMetadataAndRelaysMap(pubkey) diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts index 0ed4054..474e8dc 100644 --- a/src/utils/nostr.ts +++ b/src/utils/nostr.ts @@ -363,11 +363,21 @@ export const createWrap = (unsignedEvent: UnsignedEvent, receiver: string) => { * @returns The user application data or null if an error occurs or no data is found. */ export const getUsersAppData = async (): Promise => { + // Get an instance of the NostrController + const nostrController = NostrController.getInstance() + // Initialize an array to hold relay URLs const relays: string[] = [] // Retrieve the user's public key and relay map from the Redux store const usersPubkey = store.getState().auth.usersPubkey! + + // Decryption can fail down in the code if extension options changed + // Forcefully log out the user if we detect missmatch between pubkeys + if (usersPubkey !== (await nostrController.capturePublicKey())) { + return null + } + const relayMap = store.getState().relays?.map // Check if relayMap is undefined in the Redux store @@ -448,9 +458,6 @@ export const getUsersAppData = async (): Promise => { } } - // Get an instance of the NostrController - const nostrController = NostrController.getInstance() - // Decrypt the encrypted content const decrypted = await nostrController .nip04Decrypt(usersPubkey, encryptedContent)