Refactor auth service to use provider registry

This commit is contained in:
thePR0M3TH3AN committed 2025-10-21 13:41:57 -04:00
1 parent aa752ae94b
commit 62d411e3ff
5 files changed
+220 -25

No files matched your search

+5
View File
@@ -39,6 +39,9 @@ import watchHistoryService from "./watchHistoryService.js";
import r2Service from "./services/r2Service.js";
import PlaybackService from "./services/playbackService.js";
import AuthService from "./services/authService.js";
import getAuthProvider, {
providers as authProviders,
} from "./services/authProviders/index.js";
import NwcSettingsService from "./services/nwcSettingsService.js";
import nostrService from "./services/nostrService.js";
import DiscussionCountService from "./services/discussionCountService.js";
@@ -355,6 +358,8 @@ class Application {
relayManager,
logger: devLogger,
accessControl,
authProviders,
getAuthProvider,
});
this.authEventUnsubscribes.push(
this.authService.on("auth:login", (detail) => {
+5
View File
@@ -29,6 +29,9 @@ import {
isLockdownMode,
} from "./config.js";
import AuthService from "./services/authService.js";
import getAuthProvider, {
providers as authProviders,
} from "./services/authProviders/index.js";
import { accessControl } from "./accessControl.js";
import { nostrClient } from "./nostr.js";
import { userBlocks } from "./userBlocks.js";
@@ -106,6 +109,8 @@ function getLockdownAuthService() {
relayManager,
logger: devLogger,
accessControl,
authProviders,
getAuthProvider,
});
try {
+29
View File
@@ -0,0 +1,29 @@
import nip07 from "./nip07.js";
const map = {
[(nip07 && typeof nip07.id === "string" && nip07.id.trim()) || "nip07"]: nip07,
};
if (!Object.prototype.hasOwnProperty.call(map, "nip07")) {
map.nip07 = nip07;
}
export const providers = Object.freeze({ ...map });
export function getProvider(providerId = "nip07") {
const id =
typeof providerId === "string" && providerId.trim()
? providerId.trim()
: "nip07";
const provider = providers[id];
if (!provider || typeof provider.login !== "function") {
const error = new Error(`Unknown auth provider: ${id}`);
error.code = "unknown-auth-provider";
throw error;
}
return provider;
}
export default getProvider;
+46
View File
@@ -0,0 +1,46 @@
const PROVIDER_ID = "nip07";
function normalizePubkey(result) {
if (typeof result === "string") {
return result;
}
if (!result || typeof result !== "object") {
return "";
}
if (typeof result.pubkey === "string") {
return result.pubkey;
}
if (typeof result.publicKey === "string") {
return result.publicKey;
}
return "";
}
export default {
id: PROVIDER_ID,
async login({ nostrClient, options } = {}) {
if (!nostrClient || typeof nostrClient.login !== "function") {
const error = new Error("Nostr login is not available.");
error.code = "provider-unavailable";
throw error;
}
const result = await nostrClient.login(options || {});
const pubkey = normalizePubkey(result);
const signer =
result && typeof result === "object" && result.signer
? result.signer
: null;
return {
authType: PROVIDER_ID,
pubkey,
signer,
rawResult: result,
};
},
};
+135 -25
View File
@@ -21,6 +21,9 @@ import {
getProfileCacheEntry as getCachedProfileEntry,
setProfileCacheEntry as setCachedProfileEntry,
} from "../state/cache.js";
import getDefaultAuthProvider, {
providers as defaultAuthProviders,
} from "./authProviders/index.js";
class SimpleEventEmitter {
constructor(logger = null) {
@@ -85,11 +88,17 @@ export default class AuthService {
relayManager,
logger,
accessControl,
authProviders,
getAuthProvider,
} = {}) {
this.nostrClient = nostrClient || null;
this.userBlocks = userBlocks || null;
this.relayManager = relayManager || null;
this.accessControl = accessControl || null;
this.authProviders =
authProviders && typeof authProviders === "object"
? authProviders
: defaultAuthProviders;
if (typeof logger === "function") {
this.logger = logger;
@@ -106,6 +115,40 @@ export default class AuthService {
userLogger.warn("[AuthService] logger threw", logError);
}
});
if (typeof getAuthProvider === "function") {
this.resolveAuthProvider = (providerId) => {
const normalizedId = this.normalizeProviderId(providerId);
const provider = getAuthProvider(normalizedId);
if (!provider || typeof provider.login !== "function") {
const error = new Error(`Unknown auth provider: ${normalizedId}`);
error.code = "unknown-auth-provider";
throw error;
}
return provider;
};
} else {
this.resolveAuthProvider = (providerId) => {
const normalizedId = this.normalizeProviderId(providerId);
const registry = this.authProviders || defaultAuthProviders;
const provider =
registry && typeof registry === "object"
? registry[normalizedId]
: null;
if (provider && typeof provider.login === "function") {
return provider;
}
return getDefaultAuthProvider(normalizedId);
};
}
}
normalizeProviderId(providerId) {
return typeof providerId === "string" && providerId.trim()
? providerId.trim()
: "nip07";
}
log(...args) {
@@ -252,42 +295,85 @@ export default class AuthService {
return null;
}
async requestLogin(options = {}) {
if (!this.nostrClient || typeof this.nostrClient.login !== "function") {
throw new Error("Nostr login is not available.");
}
async requestLogin(rawOptions = {}) {
const normalizedOptions =
rawOptions && typeof rawOptions === "object" ? { ...rawOptions } : {};
const {
providerId: requestedProviderId = "nip07",
persistActive,
autoApply,
...providerOptions
} = normalizedOptions;
const result = await this.nostrClient.login(options);
const permissionResult = await requestDefaultExtensionPermissions();
if (!permissionResult.ok) {
const error = new Error(
"The NIP-07 extension denied the permission request required to finish logging in.",
);
error.code = "extension-permission-denied";
error.cause = permissionResult.error;
throw error;
}
const providerId = this.normalizeProviderId(requestedProviderId);
const provider = this.resolveAuthProvider(providerId);
const providerResult = await provider.login({
nostrClient: this.nostrClient,
options: providerOptions,
});
const rawPubkey =
providerResult && typeof providerResult === "object"
? providerResult.pubkey
: providerResult;
const pubkey =
typeof result === "string"
? result
: result && typeof result === "object"
? result.pubkey || result.publicKey || ""
typeof rawPubkey === "string"
? rawPubkey
: providerResult && typeof providerResult === "object"
? providerResult.publicKey || ""
: "";
const authTypeCandidate =
providerResult && typeof providerResult === "object"
? providerResult.authType
: null;
const authType =
typeof authTypeCandidate === "string" && authTypeCandidate.trim()
? authTypeCandidate.trim()
: providerId;
const signer =
providerResult && typeof providerResult === "object"
? providerResult.signer || null
: null;
const detailAuthType =
typeof authType === "string" && authType.trim() ? authType.trim() : "nip07";
if (authType === "nip07") {
const permissionResult = await requestDefaultExtensionPermissions();
if (!permissionResult.ok) {
const error = new Error(
"The NIP-07 extension denied the permission request required to finish logging in.",
);
error.code = "extension-permission-denied";
error.cause = permissionResult.error;
throw error;
}
}
const trimmed = typeof pubkey === "string" ? pubkey.trim() : "";
if (!trimmed) {
return { pubkey: null };
return { pubkey: null, providerId, authType: detailAuthType, signer };
}
if (options?.autoApply === false) {
return { pubkey: trimmed };
if (autoApply === false) {
return { pubkey: trimmed, providerId, authType: detailAuthType, signer };
}
const detail = await this.login(trimmed, {
persistActive: options?.persistActive !== false,
persistActive: persistActive !== false,
providerId,
authType: detailAuthType,
signer,
providerResult,
});
return { pubkey: trimmed, detail };
return {
pubkey: trimmed,
detail,
providerId,
authType: detailAuthType,
signer,
};
}
async handleUploadSubmit(payload, { publish } = {}) {
@@ -311,9 +397,24 @@ export default class AuthService {
async login(pubkey, options = {}) {
const normalizedOptions =
options && typeof options === "object" ? options : { persistActive: true };
options && typeof options === "object" ? { ...options } : {};
const persistActive =
normalizedOptions.persistActive === false ? false : true;
const providerId = this.normalizeProviderId(normalizedOptions.providerId);
const authTypeCandidate =
typeof normalizedOptions.authType === "string" && normalizedOptions.authType.trim()
? normalizedOptions.authType.trim()
: providerId;
const authType = authTypeCandidate || "nip07";
const signer = Object.prototype.hasOwnProperty.call(normalizedOptions, "signer")
? normalizedOptions.signer ?? null
: null;
const providerResult = Object.prototype.hasOwnProperty.call(
normalizedOptions,
"providerResult",
)
? normalizedOptions.providerResult ?? null
: null;
const previousPubkey = this.normalizeHexPubkey(getPubkey()) || getPubkey();
const normalized = this.normalizeHexPubkey(pubkey);
@@ -376,6 +477,11 @@ export default class AuthService {
setCurrentUserNpub(npub);
let savedProfilesMutated = false;
const entryAuthType =
typeof authType === "string" && authType.trim()
? authType.trim()
: "nip07";
mutateSavedProfiles((profiles) => {
const draft = Array.isArray(profiles) ? profiles.slice() : [];
const existingIndex = draft.findIndex((entry) => {
@@ -391,7 +497,7 @@ export default class AuthService {
npub: npub || (cachedProfile.npub ?? null),
name: cachedProfile.name || draft[existingIndex]?.name || "",
picture: cachedProfile.picture || draft[existingIndex]?.picture || "",
authType: "nip07",
authType: entryAuthType,
};
if (existingIndex >= 0) {
@@ -438,6 +544,10 @@ export default class AuthService {
identityChanged,
savedProfiles: this.cloneSavedProfiles(),
activeProfilePubkey: getActiveProfilePubkey(),
providerId,
authType: entryAuthType,
signer,
providerResult,
postLogin: {
pubkey: normalizedLoginPubkey,
blocksLoaded: false,