From 9157afd6881857ef2e475cc1f054e8bc96b7e269 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:32:46 +0000 Subject: [PATCH 1/8] Initial plan From 3ba7daeddbe98ec48e4c833599105afd8995d328 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:38:42 +0000 Subject: [PATCH 2/8] Initial plan: Add Microsoft OAuth support to Firebase authentication Co-authored-by: dharmesh-hemaram <2290202+dharmesh-hemaram@users.noreply.github.com> --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 1f738d87..25fe3e92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45017,6 +45017,7 @@ "@dhruv-techapps/core-service": "0.0.1", "@dhruv-techapps/shared-google-oauth": "0.0.1", "@firebase/app": "0.14.1", + "@firebase/util": "1.13.0", "@swc/helpers": "~0.5.11", "firebase": "^12.1.0" } From c16e39ba4f458325b1daf2772cbdb9fd50929918 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:45:19 +0000 Subject: [PATCH 3/8] Add Microsoft OAuth package and Firebase Microsoft authentication support Co-authored-by: dharmesh-hemaram <2290202+dharmesh-hemaram@users.noreply.github.com> --- packages/shared/firebase-oauth/package.json | 1 + packages/shared/firebase-oauth/src/index.ts | 2 + .../firebase-microsoft-oauth-background.ts | 79 +++++++++ .../lib/firebase-multi-oauth-background.ts | 155 ++++++++++++++++++ .../src/lib/firebase-oauth.service.ts | 18 +- .../src/lib/firebase-oauth.types.ts | 5 + packages/shared/firebase-oauth/tsconfig.json | 3 + .../shared/firebase-oauth/tsconfig.lib.json | 3 + packages/shared/microsoft-oauth/package.json | 27 +++ packages/shared/microsoft-oauth/project.json | 19 +++ packages/shared/microsoft-oauth/src/index.ts | 6 + .../src/lib/microsoft-oauth-background.ts | 101 ++++++++++++ .../src/lib/microsoft-oauth.constant.ts | 1 + .../src/lib/microsoft-oauth.service.ts | 29 ++++ .../src/lib/microsoft-oauth.types.ts | 20 +++ packages/shared/microsoft-oauth/tsconfig.json | 22 +++ .../shared/microsoft-oauth/tsconfig.lib.json | 39 +++++ .../shared/microsoft-oauth/tsconfig.spec.json | 28 ++++ 18 files changed, 553 insertions(+), 5 deletions(-) create mode 100644 packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts create mode 100644 packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts create mode 100644 packages/shared/microsoft-oauth/package.json create mode 100644 packages/shared/microsoft-oauth/project.json create mode 100644 packages/shared/microsoft-oauth/src/index.ts create mode 100644 packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts create mode 100644 packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts create mode 100644 packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts create mode 100644 packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts create mode 100644 packages/shared/microsoft-oauth/tsconfig.json create mode 100644 packages/shared/microsoft-oauth/tsconfig.lib.json create mode 100644 packages/shared/microsoft-oauth/tsconfig.spec.json diff --git a/packages/shared/firebase-oauth/package.json b/packages/shared/firebase-oauth/package.json index 2748ee94..6cab7fe1 100644 --- a/packages/shared/firebase-oauth/package.json +++ b/packages/shared/firebase-oauth/package.json @@ -21,6 +21,7 @@ "dependencies": { "@swc/helpers": "~0.5.11", "@dhruv-techapps/shared-google-oauth": "0.0.1", + "@dhruv-techapps/shared-microsoft-oauth": "0.0.1", "@dhruv-techapps/core-common": "0.0.1", "@dhruv-techapps/core-service": "0.0.1", "firebase": "^12.1.0", diff --git a/packages/shared/firebase-oauth/src/index.ts b/packages/shared/firebase-oauth/src/index.ts index 948f088a..10bf626a 100644 --- a/packages/shared/firebase-oauth/src/index.ts +++ b/packages/shared/firebase-oauth/src/index.ts @@ -1,4 +1,6 @@ export * from './lib/firebase-oauth-background'; +export * from './lib/firebase-microsoft-oauth-background'; +export * from './lib/firebase-multi-oauth-background'; export { RUNTIME_MESSAGE_FIREBASE_OAUTH } from './lib/firebase-oauth.constant'; export * from './lib/firebase-oauth.service'; export * from './lib/firebase-oauth.types'; diff --git a/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts b/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts new file mode 100644 index 00000000..fd3c4945 --- /dev/null +++ b/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts @@ -0,0 +1,79 @@ +import { MicrosoftOauth2Background } from '@dhruv-techapps/shared-microsoft-oauth'; +import { OAuthProvider, signInWithCredential } from 'firebase/auth/web-extension'; +import { Auth, FirebaseLoginResponse, FirebaseRole, User } from './firebase-oauth.types'; + +export class FirebaseMicrosoftOauth2Background extends MicrosoftOauth2Background { + auth; + constructor(auth: Auth, edgeClientId?: string) { + super(edgeClientId); + this.auth = auth; + } + + async firebaseIsLogin(): Promise { + return await this.auth.authStateReady().then(async () => { + return await this.#getUserAndRole(this.auth.currentUser); + }); + } + + async firebaseLogin(interactive = true): Promise { + const { token } = await this._getAuthToken({ interactive }); + if (token) { + const provider = new OAuthProvider('microsoft.com'); + const credential = OAuthProvider.credential(provider.providerId, { + accessToken: token, + }); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + throw new Error('Error getting credential'); + } + throw new Error('Error getting token'); + } + + async firebaseLogout() { + await this.logout(); + await this.auth.signOut(); + } + + async _getFirebaseHeaders(scopes?: string[], msToken?: string) { + await this.auth.authStateReady(); + const user = this.auth.currentUser; + if (!user) { + throw new Error('User not logged in'); + } + const token = await this.auth.currentUser?.getIdToken(); + const headers = new Headers({ Authorization: `Bearer ${token}` }); + if (!msToken) { + msToken = (await this._getAuthToken({ scopes })).token; + } + if (msToken) { + headers.append('X-Auth-Token', msToken); + } + return headers; + } + + async #getUserAndRole(user: User | null): Promise { + if (user) { + const decodedToken = await user.getIdTokenResult(); + return { user, role: decodedToken.claims['stripeRole'] as FirebaseRole }; + } else { + try { + const { token } = await this._getAuthToken({ interactive: false }); + if (token) { + const provider = new OAuthProvider('microsoft.com'); + const credential = OAuthProvider.credential(provider.providerId, { + accessToken: token, + }); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + } + } catch { + return user; + } + } + return user; + } +} \ No newline at end of file diff --git a/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts b/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts new file mode 100644 index 00000000..8587dcef --- /dev/null +++ b/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts @@ -0,0 +1,155 @@ +import { GoogleOauth2Background } from '@dhruv-techapps/shared-google-oauth'; +import { MicrosoftOauth2Background } from '@dhruv-techapps/shared-microsoft-oauth'; +import { GoogleAuthProvider, OAuthProvider, signInWithCredential } from 'firebase/auth/web-extension'; +import { Auth, FirebaseAuthProvider, FirebaseLoginResponse, FirebaseRole, User } from './firebase-oauth.types'; + +export class FirebaseMultiOauth2Background { + auth; + googleOauth: GoogleOauth2Background; + microsoftOauth: MicrosoftOauth2Background; + + constructor(auth: Auth, googleEdgeClientId?: string, microsoftEdgeClientId?: string) { + this.auth = auth; + this.googleOauth = new GoogleOauth2Background(googleEdgeClientId); + this.microsoftOauth = new MicrosoftOauth2Background(microsoftEdgeClientId); + } + + async firebaseIsLogin(): Promise { + return await this.auth.authStateReady().then(async () => { + return await this.#getUserAndRole(this.auth.currentUser); + }); + } + + async firebaseLogin(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE, interactive = true): Promise { + switch (provider) { + case FirebaseAuthProvider.GOOGLE: + return await this.#firebaseGoogleLogin(interactive); + case FirebaseAuthProvider.MICROSOFT: + return await this.#firebaseMicrosoftLogin(interactive); + default: + throw new Error(`Unsupported provider: ${provider}`); + } + } + + async firebaseLogout(provider?: FirebaseAuthProvider) { + if (provider === FirebaseAuthProvider.GOOGLE) { + await this.googleOauth.logout(); + } else if (provider === FirebaseAuthProvider.MICROSOFT) { + await this.microsoftOauth.logout(); + } else { + // Logout from both providers if no specific provider is given + try { + await this.googleOauth.logout(); + } catch { + // Ignore errors for Google logout + } + try { + await this.microsoftOauth.logout(); + } catch { + // Ignore errors for Microsoft logout + } + } + await this.auth.signOut(); + } + + async _getFirebaseHeaders(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE, scopes?: string[], token?: string) { + await this.auth.authStateReady(); + const user = this.auth.currentUser; + if (!user) { + throw new Error('User not logged in'); + } + const firebaseToken = await this.auth.currentUser?.getIdToken(); + const headers = new Headers({ Authorization: `Bearer ${firebaseToken}` }); + + if (!token) { + if (provider === FirebaseAuthProvider.GOOGLE) { + token = (await this.googleOauth._getAuthToken({ scopes })).token; + } else if (provider === FirebaseAuthProvider.MICROSOFT) { + token = (await this.microsoftOauth._getAuthToken({ scopes })).token; + } + } + if (token) { + headers.append('X-Auth-Token', token); + } + return headers; + } + + async #firebaseGoogleLogin(interactive = true): Promise { + const { token } = await this.googleOauth._getAuthToken({ interactive }); + if (token) { + const credential = GoogleAuthProvider.credential(null, token); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + throw new Error('Error getting Google credential'); + } + throw new Error('Error getting Google token'); + } + + async #firebaseMicrosoftLogin(interactive = true): Promise { + const { token } = await this.microsoftOauth._getAuthToken({ interactive }); + if (token) { + const provider = new OAuthProvider('microsoft.com'); + const credential = OAuthProvider.credential(provider.providerId, { + accessToken: token, + }); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + throw new Error('Error getting Microsoft credential'); + } + throw new Error('Error getting Microsoft token'); + } + + async #getUserAndRole(user: User | null): Promise { + if (user) { + const decodedToken = await user.getIdTokenResult(); + return { user, role: decodedToken.claims['stripeRole'] as FirebaseRole }; + } else { + // Try both Google and Microsoft authentication + try { + const googleResult = await this.#tryGoogleAuth(); + if (googleResult) return googleResult; + } catch { + // Continue to try Microsoft + } + + try { + const microsoftResult = await this.#tryMicrosoftAuth(); + if (microsoftResult) return microsoftResult; + } catch { + // Continue + } + } + return user; + } + + async #tryGoogleAuth(): Promise { + const { token } = await this.googleOauth._getAuthToken({ interactive: false }); + if (token) { + const credential = GoogleAuthProvider.credential(null, token); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + } + return null; + } + + async #tryMicrosoftAuth(): Promise { + const { token } = await this.microsoftOauth._getAuthToken({ interactive: false }); + if (token) { + const provider = new OAuthProvider('microsoft.com'); + const credential = OAuthProvider.credential(provider.providerId, { + accessToken: token, + }); + if (credential) { + const { user } = await signInWithCredential(this.auth, credential); + return await this.#getUserAndRole(user); + } + } + return null; + } +} \ No newline at end of file diff --git a/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts b/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts index 13d4b834..03ce6126 100644 --- a/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts +++ b/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts @@ -1,18 +1,26 @@ import { RuntimeMessageRequest } from '@dhruv-techapps/core-common'; import { CoreService } from '@dhruv-techapps/core-service'; import { RUNTIME_MESSAGE_FIREBASE_OAUTH } from './firebase-oauth.constant'; -import { FirebaseLoginResponse } from './firebase-oauth.types'; +import { FirebaseAuthProvider, FirebaseLoginResponse } from './firebase-oauth.types'; export class FirebaseOauthService extends CoreService { static async isLogin() { return await this.message({ messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseIsLogin' }); } - static async login() { - return await this.message({ messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseLogin' }); + static async login(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE) { + return await this.message, FirebaseLoginResponse>({ + messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, + methodName: 'firebaseLogin', + message: provider + }); } - static async logout() { - return await this.message({ messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseLogout' }); + static async logout(provider?: FirebaseAuthProvider) { + return await this.message, void>({ + messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, + methodName: 'firebaseLogout', + message: provider + }); } } diff --git a/packages/shared/firebase-oauth/src/lib/firebase-oauth.types.ts b/packages/shared/firebase-oauth/src/lib/firebase-oauth.types.ts index eb384f48..52e8a631 100644 --- a/packages/shared/firebase-oauth/src/lib/firebase-oauth.types.ts +++ b/packages/shared/firebase-oauth/src/lib/firebase-oauth.types.ts @@ -2,6 +2,11 @@ import { FirebaseApp } from '@firebase/app'; import { CompleteFn, ErrorFn, NextFn, Observer, Unsubscribe } from '@firebase/util'; export type FirebaseRole = 'pro' | 'discord' | 'sheets' | 'vision' | 'chatgpt'; +export enum FirebaseAuthProvider { + GOOGLE = 'google', + MICROSOFT = 'microsoft' +} + export type FirebaseLoginResponse = { user: User | null; role: FirebaseRole; diff --git a/packages/shared/firebase-oauth/tsconfig.json b/packages/shared/firebase-oauth/tsconfig.json index b98bee6c..2a95e344 100644 --- a/packages/shared/firebase-oauth/tsconfig.json +++ b/packages/shared/firebase-oauth/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../google-oauth" }, + { + "path": "../microsoft-oauth" + }, { "path": "./tsconfig.lib.json" }, diff --git a/packages/shared/firebase-oauth/tsconfig.lib.json b/packages/shared/firebase-oauth/tsconfig.lib.json index 488e0d20..af5218ba 100644 --- a/packages/shared/firebase-oauth/tsconfig.lib.json +++ b/packages/shared/firebase-oauth/tsconfig.lib.json @@ -20,6 +20,9 @@ }, { "path": "../google-oauth/tsconfig.lib.json" + }, + { + "path": "../microsoft-oauth/tsconfig.lib.json" } ], "exclude": [ diff --git a/packages/shared/microsoft-oauth/package.json b/packages/shared/microsoft-oauth/package.json new file mode 100644 index 00000000..525e4cf8 --- /dev/null +++ b/packages/shared/microsoft-oauth/package.json @@ -0,0 +1,27 @@ +{ + "name": "@dhruv-techapps/shared-microsoft-oauth", + "version": "0.0.1", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "development": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "!**/*.tsbuildinfo" + ], + "dependencies": { + "@swc/helpers": "~0.5.11", + "@dhruv-techapps/core-common": "0.0.1", + "@dhruv-techapps/core-service": "0.0.1", + "@dhruv-techapps/shared-notifications": "0.0.1" + } +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/project.json b/packages/shared/microsoft-oauth/project.json new file mode 100644 index 00000000..42a0258e --- /dev/null +++ b/packages/shared/microsoft-oauth/project.json @@ -0,0 +1,19 @@ +{ + "name": "shared-microsoft-oauth", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/shared/microsoft-oauth/src", + "projectType": "library", + "tags": ["scope:shared"], + "targets": { + "build": { + "executor": "@nx/js:swc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "packages/shared/microsoft-oauth/dist", + "main": "packages/shared/microsoft-oauth/src/index.ts", + "tsConfig": "packages/shared/microsoft-oauth/tsconfig.lib.json", + "stripLeadingPaths": true + } + } + } +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/src/index.ts b/packages/shared/microsoft-oauth/src/index.ts new file mode 100644 index 00000000..61047a2d --- /dev/null +++ b/packages/shared/microsoft-oauth/src/index.ts @@ -0,0 +1,6 @@ +/// + +export * from './lib/microsoft-oauth-background'; +export { RUNTIME_MESSAGE_MICROSOFT_OAUTH } from './lib/microsoft-oauth.constant'; +export * from './lib/microsoft-oauth.service'; +export * from './lib/microsoft-oauth.types'; \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts new file mode 100644 index 00000000..2bf450b9 --- /dev/null +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts @@ -0,0 +1,101 @@ +import { BROWSER } from '@dhruv-techapps/core-common'; +import { NotificationHandler } from '@dhruv-techapps/shared-notifications'; +import { MICROSOFT_SCOPES, MicrosoftOauth2LoginResponse } from './microsoft-oauth.types'; + +const NOTIFICATIONS_TITLE = 'Microsoft OAuth'; +const NOTIFICATIONS_ID = 'authentication'; + +export class MicrosoftOauth2Background { + edgeClientId: string | undefined; + constructor(edgeClientId?: string) { + this.edgeClientId = edgeClientId; + } + + async login(scopes?: Array): Promise { + try { + const result = await this._getAuthToken({ scopes, interactive: true }); + return result; + } catch (error) { + if (error instanceof Error) { + NotificationHandler.notify(NOTIFICATIONS_ID, NOTIFICATIONS_TITLE, error.message); + } + await this.logout(scopes); + throw error; + } + } + + async logout(scopes?: Array) { + const { token } = await this._getAuthToken({ scopes, interactive: false }); + if (token) { + await chrome.identity.removeCachedAuthToken({ token }); + } + return true; + } + + async userInfo() { + const { token } = await this._getAuthToken({}); + const headers = new Headers({ Authorization: `Bearer ${token}` }); + let response = await fetch(`https://graph.microsoft.com/v1.0/me`, { headers }); + response = await response.json(); + return response; + } + + async hasAccess(scopes: Array) { + const result = await this._getAuthToken({ scopes, interactive: false }); + return result; + } + + async _getAuthToken({ scopes, interactive }: { scopes?: Array; interactive?: boolean }): Promise { + scopes = scopes || [MICROSOFT_SCOPES.PROFILE, MICROSOFT_SCOPES.EMAIL]; + if (!scopes || scopes.length === 0) { + throw new Error('No scopes found'); + } + try { + const result = await this.#launchWebAuthFlow(scopes, interactive); + return result; + } catch (error) { + if (error instanceof Error && interactive) { + if (error.message === 'Invalid Credentials' || error.message === 'invalid authentication credentials') { + await this.logout(); + const result = await this._getAuthToken({ scopes, interactive }); + return result; + } else { + NotificationHandler.notify(NOTIFICATIONS_ID, NOTIFICATIONS_TITLE, error.message); + } + } + throw error; + } + } + + async #launchWebAuthFlow(scopes: string[], interactive?: boolean) { + if (this.edgeClientId === undefined) { + throw new Error('Microsoft client id not found'); + } + + const url = new URL('https://login.microsoftonline.com/common/oauth2/v2.0/authorize'); + url.searchParams.append('client_id', this.edgeClientId); + url.searchParams.append('redirect_uri', chrome.identity.getRedirectURL()); + url.searchParams.append('response_type', 'token'); + url.searchParams.append('scope', scopes.join(' ')); + url.searchParams.append('response_mode', 'fragment'); + + const result = await chrome.identity.launchWebAuthFlow({ + url: url.href, + interactive + }); + if (result) { + const url = new URL(result); + const token = url?.hash + ?.split('&') + .find((param) => param.startsWith('#access_token=')) + ?.split('=')[1]; + if (token) { + return { token, grantedScopes: scopes }; + } + NotificationHandler.notify(NOTIFICATIONS_ID, NOTIFICATIONS_TITLE, 'Token not found'); + throw new Error('Token not found'); + } + NotificationHandler.notify(NOTIFICATIONS_ID, NOTIFICATIONS_TITLE, 'Error while retrieving token'); + throw new Error('Error while retrieving token'); + } +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts new file mode 100644 index 00000000..d5a475d1 --- /dev/null +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts @@ -0,0 +1 @@ +export const RUNTIME_MESSAGE_MICROSOFT_OAUTH = 'microsoft-oauth'; \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts new file mode 100644 index 00000000..5901cfa3 --- /dev/null +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts @@ -0,0 +1,29 @@ +import { RuntimeMessageRequest } from '@dhruv-techapps/core-common'; +import { CoreService } from '@dhruv-techapps/core-service'; +import { RUNTIME_MESSAGE_MICROSOFT_OAUTH } from './microsoft-oauth.constant'; +import { MICROSOFT_SCOPES, MicrosoftOauth2LoginResponse, MicrosoftOauth2RemoveResponse } from './microsoft-oauth.types'; + +export class MicrosoftOauthService extends CoreService { + static async login(scopes: Array) { + return await this.message>, MicrosoftOauth2LoginResponse>({ + messenger: RUNTIME_MESSAGE_MICROSOFT_OAUTH, + methodName: 'login', + message: scopes + }); + } + static async logout(scopes: Array) { + return await this.message>, MicrosoftOauth2RemoveResponse>({ + messenger: RUNTIME_MESSAGE_MICROSOFT_OAUTH, + methodName: 'logout', + message: scopes + }); + } + + static async hasAccess(scopes: Array) { + return await this.message>, MicrosoftOauth2LoginResponse>({ messenger: RUNTIME_MESSAGE_MICROSOFT_OAUTH, methodName: 'hasAccess', message: scopes }); + } + + static async userInfo() { + return await this.message({ messenger: RUNTIME_MESSAGE_MICROSOFT_OAUTH, methodName: 'userInfo' }); + } +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts new file mode 100644 index 00000000..93d6dde1 --- /dev/null +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts @@ -0,0 +1,20 @@ +import { RESPONSE_CODE } from '@dhruv-techapps/core-common'; + +export enum MICROSOFT_SCOPES { + PROFILE = 'https://graph.microsoft.com/User.Read', + EMAIL = 'https://graph.microsoft.com/User.Read', + OPENID = 'openid', + OFFLINE_ACCESS = 'offline_access' +} + +export interface MicrosoftOauth2LoginResponse { + token?: string; + grantedScopes?: string[]; +} + +export type MicrosoftOauth2RemoveResponse = RESPONSE_CODE.REMOVED; + +export abstract class MicrosoftOauth2Service { + abstract login(): Promise; + abstract remove(): Promise; +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/tsconfig.json b/packages/shared/microsoft-oauth/tsconfig.json new file mode 100644 index 00000000..48f27607 --- /dev/null +++ b/packages/shared/microsoft-oauth/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "../../core/service" + }, + { + "path": "../notifications" + }, + { + "path": "../../core/common" + }, + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/tsconfig.lib.json b/packages/shared/microsoft-oauth/tsconfig.lib.json new file mode 100644 index 00000000..64cf7709 --- /dev/null +++ b/packages/shared/microsoft-oauth/tsconfig.lib.json @@ -0,0 +1,39 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "lib": ["dom"], + "baseUrl": ".", + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", + "emitDeclarationOnly": true, + "forceConsistentCasingInFileNames": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "references": [ + { + "path": "../../core/service/tsconfig.lib.json" + }, + { + "path": "../notifications/tsconfig.lib.json" + }, + { + "path": "../../core/common/tsconfig.lib.json" + } + ], + "exclude": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" + ] +} \ No newline at end of file diff --git a/packages/shared/microsoft-oauth/tsconfig.spec.json b/packages/shared/microsoft-oauth/tsconfig.spec.json new file mode 100644 index 00000000..6ac5b70e --- /dev/null +++ b/packages/shared/microsoft-oauth/tsconfig.spec.json @@ -0,0 +1,28 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/vitest", + "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"], + "forceConsistentCasingInFileNames": true + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} \ No newline at end of file From 15fdf23a026f6404ccfbf6eddd3e11f4f9ed121b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:49:32 +0000 Subject: [PATCH 4/8] Add comprehensive documentation and README for Microsoft OAuth integration Co-authored-by: dharmesh-hemaram <2290202+dharmesh-hemaram@users.noreply.github.com> --- packages/shared/microsoft-oauth/README.md | 145 ++++++ .../content/docs/authentication/examples.mdx | 477 ++++++++++++++++++ .../src/content/docs/authentication/index.mdx | 135 +++++ .../docs/authentication/microsoft-oauth.mdx | 145 ++++++ 4 files changed, 902 insertions(+) create mode 100644 packages/shared/microsoft-oauth/README.md create mode 100644 site/src/content/docs/authentication/examples.mdx create mode 100644 site/src/content/docs/authentication/index.mdx create mode 100644 site/src/content/docs/authentication/microsoft-oauth.mdx diff --git a/packages/shared/microsoft-oauth/README.md b/packages/shared/microsoft-oauth/README.md new file mode 100644 index 00000000..2565e690 --- /dev/null +++ b/packages/shared/microsoft-oauth/README.md @@ -0,0 +1,145 @@ +# Microsoft OAuth Package + +A Microsoft OAuth 2.0 authentication package for Auto Clicker AutoFill web extensions. + +## Overview + +This package provides Microsoft OAuth authentication functionality for Chrome web extensions, enabling secure sign-in with Microsoft accounts. + +## Features + +- Microsoft OAuth 2.0 authentication flow +- Chrome extension identity API integration +- Token management and refresh +- User information retrieval from Microsoft Graph +- Scope-based permission handling + +## Installation + +```bash +npm install @dhruv-techapps/shared-microsoft-oauth +``` + +## Usage + +### Basic Authentication + +```typescript +import { MicrosoftOauth2Background, MICROSOFT_SCOPES } from '@dhruv-techapps/shared-microsoft-oauth'; + +// Initialize with Microsoft client ID +const microsoftAuth = new MicrosoftOauth2Background('your-microsoft-client-id'); + +// Login +const result = await microsoftAuth.login([MICROSOFT_SCOPES.PROFILE]); +console.log('Access token:', result.token); + +// Get user info +const userInfo = await microsoftAuth.userInfo(); +console.log('User:', userInfo); + +// Logout +await microsoftAuth.logout(); +``` + +### Service Layer + +```typescript +import { MicrosoftOauthService, MICROSOFT_SCOPES } from '@dhruv-techapps/shared-microsoft-oauth'; + +// For cross-context messaging in extensions +await MicrosoftOauthService.login([MICROSOFT_SCOPES.PROFILE]); +const userInfo = await MicrosoftOauthService.userInfo(); +await MicrosoftOauthService.logout([MICROSOFT_SCOPES.PROFILE]); +``` + +## Configuration + +### Azure App Registration + +1. Register your application in the [Azure Portal](https://portal.azure.com) +2. Configure redirect URI: `https://.chromiumapp.org/` +3. Note the Application (client) ID + +### Extension Manifest + +```json +{ + "permissions": ["identity"], + "oauth2": { + "client_id": "your-microsoft-client-id", + "scopes": [ + "https://graph.microsoft.com/User.Read", + "openid" + ] + } +} +``` + +## Available Scopes + +- `MICROSOFT_SCOPES.PROFILE` - Read user profile +- `MICROSOFT_SCOPES.EMAIL` - Read user email +- `MICROSOFT_SCOPES.OPENID` - OpenID Connect +- `MICROSOFT_SCOPES.OFFLINE_ACCESS` - Refresh token access + +## API Reference + +### MicrosoftOauth2Background + +Main class for handling Microsoft OAuth in background scripts. + +#### Methods + +- `login(scopes?)` - Initiate login flow +- `logout(scopes?)` - Remove cached tokens +- `userInfo()` - Get user information +- `hasAccess(scopes)` - Check token validity + +### MicrosoftOauthService + +Service layer for cross-context messaging. + +#### Static Methods + +- `login(scopes)` - Login via messaging +- `logout(scopes)` - Logout via messaging +- `userInfo()` - Get user info via messaging +- `hasAccess(scopes)` - Check access via messaging + +## Error Handling + +The package includes comprehensive error handling for common OAuth scenarios: + +- Missing client ID +- Network errors +- User cancellation +- Invalid credentials + +## Browser Support + +- Chrome extensions +- Edge extensions +- Other Chromium-based browsers + +## Integration with Firebase + +For Firebase Authentication integration, use alongside `@dhruv-techapps/shared-firebase-oauth`: + +```typescript +import { FirebaseMicrosoftOauth2Background } from '@dhruv-techapps/shared-firebase-oauth'; + +const firebaseAuth = new FirebaseMicrosoftOauth2Background(auth, 'microsoft-client-id'); +await firebaseAuth.firebaseLogin(); +``` + +## Security Considerations + +- Client IDs should be stored securely +- Use appropriate scopes for your use case +- Implement proper token refresh logic +- Handle authentication errors gracefully + +## Contributing + +This package is part of the Auto Clicker AutoFill monorepo. See the main repository for contribution guidelines. \ No newline at end of file diff --git a/site/src/content/docs/authentication/examples.mdx b/site/src/content/docs/authentication/examples.mdx new file mode 100644 index 00000000..02788ef2 --- /dev/null +++ b/site/src/content/docs/authentication/examples.mdx @@ -0,0 +1,477 @@ +--- +title: Authentication · Usage Examples +description: Practical examples of implementing Microsoft and Google OAuth in Auto Clicker AutoFill extensions. +tags: [authentication, examples, microsoft-oauth, google-oauth, firebase, tutorial] +toc: true +--- + +## Authentication Usage Examples + +This page provides practical examples of implementing authentication in Auto Clicker AutoFill extensions using both Google and Microsoft OAuth providers. + +### Basic Setup + +#### Extension Background Script + +```typescript +// background.ts +import { getAuth } from 'firebase/auth/web-extension'; +import { initializeApp } from 'firebase/app'; +import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; + +// Initialize Firebase +const firebaseConfig = { + apiKey: "your-api-key", + authDomain: "your-project.firebaseapp.com", + // ... other config +}; + +const app = initializeApp(firebaseConfig); +const auth = getAuth(app); + +// Initialize multi-provider authentication +const authHandler = new FirebaseMultiOauth2Background( + auth, + 'your-google-client-id', // Google OAuth client ID + 'your-microsoft-client-id' // Microsoft OAuth client ID +); + +// Handle authentication messages from content scripts +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + switch (request.action) { + case 'microsoft-login': + authHandler.firebaseLogin(FirebaseAuthProvider.MICROSOFT) + .then(result => sendResponse({ success: true, user: result?.user })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; + + case 'google-login': + authHandler.firebaseLogin(FirebaseAuthProvider.GOOGLE) + .then(result => sendResponse({ success: true, user: result?.user })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; + + case 'logout': + authHandler.firebaseLogout() + .then(() => sendResponse({ success: true })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; + + case 'check-auth': + authHandler.firebaseIsLogin() + .then(result => sendResponse({ + success: true, + isLoggedIn: !!result?.user, + user: result?.user, + role: result?.role + })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; + } +}); +``` + +#### Content Script Integration + +```typescript +// content-script.ts +class AuthenticationManager { + async loginWithMicrosoft(): Promise { + try { + const response = await chrome.runtime.sendMessage({ action: 'microsoft-login' }); + if (response.success) { + console.log('Microsoft login successful:', response.user); + return true; + } else { + console.error('Microsoft login failed:', response.error); + return false; + } + } catch (error) { + console.error('Error during Microsoft login:', error); + return false; + } + } + + async loginWithGoogle(): Promise { + try { + const response = await chrome.runtime.sendMessage({ action: 'google-login' }); + if (response.success) { + console.log('Google login successful:', response.user); + return true; + } else { + console.error('Google login failed:', response.error); + return false; + } + } catch (error) { + console.error('Error during Google login:', error); + return false; + } + } + + async logout(): Promise { + try { + const response = await chrome.runtime.sendMessage({ action: 'logout' }); + return response.success; + } catch (error) { + console.error('Error during logout:', error); + return false; + } + } + + async checkAuthStatus(): Promise<{ isLoggedIn: boolean; user?: any; role?: string }> { + try { + const response = await chrome.runtime.sendMessage({ action: 'check-auth' }); + if (response.success) { + return { + isLoggedIn: response.isLoggedIn, + user: response.user, + role: response.role + }; + } + } catch (error) { + console.error('Error checking auth status:', error); + } + return { isLoggedIn: false }; + } +} + +// Usage in content script +const authManager = new AuthenticationManager(); + +// Check authentication status on page load +authManager.checkAuthStatus().then(status => { + if (status.isLoggedIn) { + console.log('User is logged in:', status.user); + // Initialize authenticated features + } else { + console.log('User is not logged in'); + // Show login options + } +}); +``` + +### Advanced Examples + +#### Provider-Specific Authentication + +```typescript +// separate-providers.ts +import { + FirebaseOauth2Background, + FirebaseMicrosoftOauth2Background, + FirebaseAuthProvider +} from '@dhruv-techapps/shared-firebase-oauth'; + +class ProviderSpecificAuth { + private googleAuth: FirebaseOauth2Background; + private microsoftAuth: FirebaseMicrosoftOauth2Background; + + constructor(auth: any) { + this.googleAuth = new FirebaseOauth2Background(auth, 'google-client-id'); + this.microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'microsoft-client-id'); + } + + async loginWithPreferredProvider(provider: 'google' | 'microsoft') { + try { + let result; + + if (provider === 'google') { + result = await this.googleAuth.firebaseLogin(); + console.log('Google authentication result:', result); + } else { + result = await this.microsoftAuth.firebaseLogin(); + console.log('Microsoft authentication result:', result); + } + + return result; + } catch (error) { + console.error(`${provider} authentication failed:`, error); + throw error; + } + } + + async getAuthHeaders(provider: 'google' | 'microsoft', scopes?: string[]) { + try { + if (provider === 'google') { + return await this.googleAuth._getFirebaseHeaders(scopes); + } else { + return await this.microsoftAuth._getFirebaseHeaders(scopes); + } + } catch (error) { + console.error(`Failed to get ${provider} auth headers:`, error); + throw error; + } + } +} +``` + +#### Service Layer Usage + +```typescript +// service-layer-example.ts +import { + FirebaseOauthService, + GoogleOauthService, + MicrosoftOauthService, + FirebaseAuthProvider, + GOOGLE_SCOPES, + MICROSOFT_SCOPES +} from '@dhruv-techapps/shared-firebase-oauth'; + +class AuthService { + // Firebase authentication with provider choice + static async authenticateWithFirebase(provider: FirebaseAuthProvider) { + try { + const result = await FirebaseOauthService.login(provider); + console.log('Firebase authentication successful:', result); + return result; + } catch (error) { + console.error('Firebase authentication failed:', error); + throw error; + } + } + + // Direct Google OAuth (without Firebase) + static async authenticateWithGoogle() { + try { + const result = await GoogleOauthService.login([ + GOOGLE_SCOPES.PROFILE, + GOOGLE_SCOPES.EMAIL + ]); + console.log('Google OAuth successful:', result); + return result; + } catch (error) { + console.error('Google OAuth failed:', error); + throw error; + } + } + + // Direct Microsoft OAuth (without Firebase) + static async authenticateWithMicrosoft() { + try { + const result = await MicrosoftOauthService.login([ + MICROSOFT_SCOPES.PROFILE, + MICROSOFT_SCOPES.EMAIL + ]); + console.log('Microsoft OAuth successful:', result); + return result; + } catch (error) { + console.error('Microsoft OAuth failed:', error); + throw error; + } + } + + // Check authentication status + static async checkStatus() { + try { + return await FirebaseOauthService.isLogin(); + } catch (error) { + console.error('Status check failed:', error); + return null; + } + } + + // Logout from all providers + static async logout() { + try { + await FirebaseOauthService.logout(); + console.log('Logout successful'); + } catch (error) { + console.error('Logout failed:', error); + throw error; + } + } +} +``` + +#### UI Integration Example + +```typescript +// ui-integration.ts +class AuthenticationUI { + private authStatus: { isLoggedIn: boolean; provider?: string; user?: any } = { isLoggedIn: false }; + + async initializeUI() { + // Check current authentication status + await this.updateAuthStatus(); + + // Render appropriate UI + this.renderAuthUI(); + + // Set up event listeners + this.setupEventListeners(); + } + + private async updateAuthStatus() { + try { + const response = await chrome.runtime.sendMessage({ action: 'check-auth' }); + if (response.success && response.isLoggedIn) { + this.authStatus = { + isLoggedIn: true, + user: response.user, + provider: this.detectProvider(response.user) + }; + } else { + this.authStatus = { isLoggedIn: false }; + } + } catch (error) { + console.error('Failed to check auth status:', error); + this.authStatus = { isLoggedIn: false }; + } + } + + private detectProvider(user: any): string { + // Detect provider based on user data + if (user?.providerData) { + for (const provider of user.providerData) { + if (provider.providerId === 'google.com') return 'Google'; + if (provider.providerId === 'microsoft.com') return 'Microsoft'; + } + } + return 'Unknown'; + } + + private renderAuthUI() { + const container = document.getElementById('auth-container'); + if (!container) return; + + if (this.authStatus.isLoggedIn) { + container.innerHTML = ` +
+

Logged in with ${this.authStatus.provider}

+

Welcome, ${this.authStatus.user?.displayName || 'User'}

+ +
+ `; + } else { + container.innerHTML = ` +
+

Please sign in to continue

+ + +
+ `; + } + } + + private setupEventListeners() { + document.addEventListener('click', async (event) => { + const target = event.target as HTMLElement; + + if (target.id === 'google-login-btn') { + await this.handleLogin('google'); + } else if (target.id === 'microsoft-login-btn') { + await this.handleLogin('microsoft'); + } else if (target.id === 'logout-btn') { + await this.handleLogout(); + } + }); + } + + private async handleLogin(provider: 'google' | 'microsoft') { + try { + const action = provider === 'google' ? 'google-login' : 'microsoft-login'; + const response = await chrome.runtime.sendMessage({ action }); + + if (response.success) { + await this.updateAuthStatus(); + this.renderAuthUI(); + console.log(`${provider} login successful`); + } else { + console.error(`${provider} login failed:`, response.error); + alert(`Login failed: ${response.error}`); + } + } catch (error) { + console.error(`Error during ${provider} login:`, error); + alert(`Login error: ${error.message}`); + } + } + + private async handleLogout() { + try { + const response = await chrome.runtime.sendMessage({ action: 'logout' }); + + if (response.success) { + this.authStatus = { isLoggedIn: false }; + this.renderAuthUI(); + console.log('Logout successful'); + } else { + console.error('Logout failed:', response.error); + } + } catch (error) { + console.error('Error during logout:', error); + } + } +} + +// Initialize UI when page loads +document.addEventListener('DOMContentLoaded', () => { + const authUI = new AuthenticationUI(); + authUI.initializeUI(); +}); +``` + +### Error Handling Examples + +```typescript +// error-handling.ts +class AuthErrorHandler { + static handleAuthError(error: any, provider: string): string { + if (error?.message) { + switch (error.message) { + case 'Microsoft client id not found': + case 'Google client id not found': + return `${provider} authentication is not configured. Please contact support.`; + + case 'User not logged in': + return 'Please sign in to continue.'; + + case 'Invalid Credentials': + case 'invalid authentication credentials': + return 'Your credentials have expired. Please sign in again.'; + + case 'Token not found': + return 'Authentication failed. Please try again.'; + + case 'Error while retrieving token': + return 'Network error during authentication. Please check your connection.'; + + default: + return `Authentication error: ${error.message}`; + } + } + + return 'An unknown authentication error occurred.'; + } + + static async withErrorHandling( + operation: () => Promise, + provider: string + ): Promise<{ success: boolean; data?: T; error?: string }> { + try { + const data = await operation(); + return { success: true, data }; + } catch (error) { + const errorMessage = this.handleAuthError(error, provider); + console.error(`${provider} operation failed:`, error); + return { success: false, error: errorMessage }; + } + } +} + +// Usage example +const result = await AuthErrorHandler.withErrorHandling( + () => FirebaseOauthService.login(FirebaseAuthProvider.MICROSOFT), + 'Microsoft' +); + +if (result.success) { + console.log('Authentication successful:', result.data); +} else { + console.error('Authentication failed:', result.error); + // Show user-friendly error message + showNotification(result.error); +} +``` + +These examples demonstrate comprehensive integration of Microsoft and Google OAuth authentication in Auto Clicker AutoFill extensions, including error handling, UI integration, and service layer usage. \ No newline at end of file diff --git a/site/src/content/docs/authentication/index.mdx b/site/src/content/docs/authentication/index.mdx new file mode 100644 index 00000000..f2f6b33c --- /dev/null +++ b/site/src/content/docs/authentication/index.mdx @@ -0,0 +1,135 @@ +--- +title: Authentication Overview +description: Overview of authentication options in Auto Clicker AutoFill, including Google and Microsoft OAuth support. +tags: [authentication, oauth, firebase, google, microsoft, login] +toc: true +--- + +## Authentication in Auto Clicker AutoFill + +Auto Clicker AutoFill supports multiple authentication providers through Firebase Authentication, enabling secure sign-in and enhanced functionality for users. + +### Supported Providers + +#### Google OAuth +- **Primary provider** for Google services integration +- Supports Google Sheets, Google Drive, and other Google APIs +- Established and fully integrated authentication flow + +#### Microsoft OAuth +- **New support** for Microsoft account authentication +- Enables integration with Microsoft Graph APIs +- Uses same Firebase Authentication infrastructure + +### Authentication Architecture + +The authentication system is built on Firebase Authentication with OAuth providers, providing: + +- **Secure Token Management**: Firebase handles token refresh and validation +- **Cross-Context Communication**: Authentication state shared across extension contexts +- **Provider Flexibility**: Support for multiple OAuth providers +- **Role-Based Access**: User roles and permissions management + +### Implementation Layers + +#### Background Services +- `GoogleOauth2Background`: Google OAuth implementation +- `MicrosoftOauth2Background`: Microsoft OAuth implementation +- `FirebaseOauth2Background`: Firebase integration for Google +- `FirebaseMicrosoftOauth2Background`: Firebase integration for Microsoft +- `FirebaseMultiOauth2Background`: Unified multi-provider support + +#### Service Layer +- `GoogleOauthService`: Google OAuth messaging service +- `MicrosoftOauthService`: Microsoft OAuth messaging service +- `FirebaseOauthService`: Firebase authentication messaging service + +#### Types and Constants +- Standardized interfaces for authentication responses +- Provider enums and constants for consistent usage +- Error handling types and messaging + +### Usage Patterns + +#### Single Provider Authentication +Use specific provider implementations for dedicated authentication: + +```typescript +// Google authentication +const googleAuth = new FirebaseOauth2Background(auth, googleClientId); +await googleAuth.firebaseLogin(); + +// Microsoft authentication +const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, microsoftClientId); +await microsoftAuth.firebaseLogin(); +``` + +#### Multi-Provider Authentication +Use the unified implementation for flexible provider support: + +```typescript +const multiAuth = new FirebaseMultiOauth2Background( + auth, + googleClientId, + microsoftClientId +); + +// Choose provider at runtime +await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE); +await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT); +``` + +### Configuration Requirements + +#### Firebase Setup +1. Enable desired OAuth providers in Firebase Console +2. Configure provider-specific settings (client IDs, secrets) +3. Set up appropriate redirect URIs for extension context + +#### Extension Manifest +Update your extension's manifest to include OAuth configuration: + +```json +{ + "oauth2": { + "client_id": "your-google-client-id", + "scopes": ["https://www.googleapis.com/auth/userinfo.profile"] + }, + "permissions": ["identity"] +} +``` + +#### Provider Registration +- **Google**: Configure in Google Cloud Console +- **Microsoft**: Register application in Azure Portal + +### Security Features + +- **Secure Token Storage**: Firebase manages token encryption and storage +- **Automatic Refresh**: Tokens are automatically refreshed before expiration +- **Scope Management**: Fine-grained permission control through OAuth scopes +- **Error Handling**: Comprehensive error handling and user feedback + +### Best Practices + +1. **Provider Selection**: Allow users to choose their preferred authentication method +2. **Graceful Fallback**: Handle authentication failures gracefully +3. **Token Validation**: Always verify authentication state before sensitive operations +4. **Logout Cleanup**: Properly clean up authentication state on logout +5. **Error Communication**: Provide clear error messages to users + +### Integration Examples + +See the following guides for specific implementation details: + +- [Microsoft OAuth Integration](./microsoft-oauth.mdx) +- [Google Sheets Authentication](../settings/google-sheets.mdx) + +### Browser Support + +Authentication is supported in: +- Chrome extensions (primary target) +- Edge extensions (with appropriate configuration) +- Other Chromium-based browsers with extension support + +The authentication system is designed to work within the constraints of web extension environments while providing secure and reliable authentication flows. \ No newline at end of file diff --git a/site/src/content/docs/authentication/microsoft-oauth.mdx b/site/src/content/docs/authentication/microsoft-oauth.mdx new file mode 100644 index 00000000..ccae827c --- /dev/null +++ b/site/src/content/docs/authentication/microsoft-oauth.mdx @@ -0,0 +1,145 @@ +--- +title: Authentication · Microsoft OAuth +description: Sign in with Microsoft using Firebase Authentication in the Auto Clicker AutoFill web extension. +tags: [authentication, microsoft-oauth, firebase, microsoft, oauth, login] +toc: true +--- + +## Microsoft OAuth Integration + +Auto Clicker AutoFill now supports Microsoft sign-in through Firebase Authentication, providing seamless integration with Microsoft accounts for enhanced functionality. + +### Overview + +The Microsoft OAuth implementation allows users to authenticate using their Microsoft accounts, similar to the existing Google sign-in functionality. This integration uses Firebase Authentication with Microsoft as an OAuth provider. + +### Features + +- **Microsoft Account Sign-In**: Users can sign in using their Microsoft accounts +- **Firebase Integration**: Leverages Firebase Authentication for secure token management +- **Extension Compatibility**: Works within the web extension's background and content script architecture +- **Unified Authentication**: Can be used alongside Google OAuth for flexible authentication options + +### Technical Implementation + +The Microsoft OAuth implementation consists of: + +- `MicrosoftOauth2Background`: Handles Microsoft OAuth flow in the extension background +- `FirebaseMicrosoftOauth2Background`: Integrates Microsoft OAuth with Firebase Authentication +- `FirebaseMultiOauth2Background`: Supports both Google and Microsoft authentication providers + +### Usage + +#### Basic Microsoft Authentication + +```typescript +import { FirebaseMicrosoftOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; + +// Initialize with Firebase Auth and Microsoft client ID +const auth = getAuth(app); +const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'your-microsoft-client-id'); + +// Sign in with Microsoft +const result = await microsoftAuth.firebaseLogin(true); +console.log('User:', result?.user); +console.log('Role:', result?.role); +``` + +#### Multi-Provider Authentication + +```typescript +import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; + +// Initialize with both Google and Microsoft client IDs +const auth = getAuth(app); +const multiAuth = new FirebaseMultiOauth2Background( + auth, + 'your-google-client-id', + 'your-microsoft-client-id' +); + +// Sign in with Microsoft +await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT); + +// Sign in with Google +await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE); + +// Check login status +const loginStatus = await multiAuth.firebaseIsLogin(); + +// Logout from specific provider +await multiAuth.firebaseLogout(FirebaseAuthProvider.MICROSOFT); +``` + +### Configuration + +#### Microsoft Azure App Registration + +1. Register your application in the [Azure portal](https://portal.azure.com) +2. Configure redirect URI to match your extension's redirect URL +3. Note the Application (client) ID for use in the extension + +#### Firebase Configuration + +1. Enable Microsoft authentication in Firebase Console +2. Configure Microsoft OAuth provider with your Azure app credentials +3. Update your extension's manifest with the Microsoft client ID + +### Scopes + +The Microsoft OAuth implementation supports the following scopes: + +- `https://graph.microsoft.com/User.Read` - Read user profile information +- `openid` - OpenID Connect authentication +- `offline_access` - Refresh token access + +### Error Handling + +The implementation includes comprehensive error handling: + +```typescript +try { + const result = await microsoftAuth.firebaseLogin(true); +} catch (error) { + if (error.message === 'Microsoft client id not found') { + // Handle missing client ID + } else if (error.message === 'User not logged in') { + // Handle authentication failure + } +} +``` + +### Service Integration + +Use the service layer for messaging between extension contexts: + +```typescript +import { FirebaseOauthService, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; + +// Service calls for cross-context communication +await FirebaseOauthService.login(FirebaseAuthProvider.MICROSOFT); +await FirebaseOauthService.logout(FirebaseAuthProvider.MICROSOFT); +const status = await FirebaseOauthService.isLogin(); +``` + +### Best Practices + +1. **Client ID Security**: Store Microsoft client IDs securely and never expose them in client-side code +2. **Error Handling**: Always wrap authentication calls in try-catch blocks +3. **Provider Selection**: Allow users to choose their preferred authentication provider +4. **Token Management**: Use Firebase's built-in token refresh mechanisms +5. **Logout Cleanup**: Properly clean up tokens when users sign out + +### Browser Compatibility + +Microsoft OAuth is supported in: +- Chrome extensions +- Edge extensions (with appropriate client ID configuration) +- Other Chromium-based browsers + +### Security Considerations + +- Microsoft tokens are handled securely through Firebase Authentication +- OAuth flow uses secure redirect URIs +- Tokens are automatically refreshed by Firebase +- Authentication state is managed consistently across extension contexts \ No newline at end of file From 2b3c7f48d3a9394f880fc7a7d41b1840d6562419 Mon Sep 17 00:00:00 2001 From: Dharmesh Date: Wed, 3 Sep 2025 15:31:29 +0530 Subject: [PATCH 5/8] Trigger Codacy re-analysis From b38ebf2ee593b2d24c0ea9365f82daafe5919ce3 Mon Sep 17 00:00:00 2001 From: Dharmesh Date: Wed, 3 Sep 2025 15:31:47 +0530 Subject: [PATCH 6/8] Codacy --- .codacy.yml | 7 --- .github/instructions/codacy.instructions.md | 65 +++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 .github/instructions/codacy.instructions.md diff --git a/.codacy.yml b/.codacy.yml index df6f4e76..9e66f759 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -1,12 +1,5 @@ exclude_paths: - 'README.md' - - '**/**/eslint.config.mjs' - - '**/**/package.json' - - '**/**/tsconfig.json' - - '**/**/vite.config.ts' - - '**/**/project.json' - - '**/**/tsconfig.*.json' - - '**/**/webpack.config.js' - 'scripts/*' - 'site/*' - 'build/*' diff --git a/.github/instructions/codacy.instructions.md b/.github/instructions/codacy.instructions.md new file mode 100644 index 00000000..cf756d15 --- /dev/null +++ b/.github/instructions/codacy.instructions.md @@ -0,0 +1,65 @@ +--- + description: Configuration for AI behavior when interacting with Codacy's MCP Server + applyTo: '**' +--- +--- +# Codacy Rules +Configuration for AI behavior when interacting with Codacy's MCP Server + +## CRITICAL: After ANY successful `edit_file` or `reapply` operation +- YOU MUST IMMEDIATELY run the `codacy_cli_analyze` tool from Codacy's MCP Server for each file that was edited, with: + - `rootPath`: set to the workspace path + - `file`: set to the path of the edited file + - `tool`: leave empty or unset +- If any issues are found in the new edits, propose and apply fixes for them. +- > NOTE: Failure to follow this rule is considered a critical error. + +## After every response +- If you made any file edits in this conversation, verify you ran `codacy_cli_analyze` tool from Codacy's MCP Server + +## When there are no Codacy MCP Server tools available, or the MCP Server is not reachable +- Suggest the user the following troubleshooting steps: + - Try to reset the MCP on the extension + - If the user is using VSCode, suggest them to review their Copilot > MCP settings in Github, under their organization or personal account. Refer them to Settings > Copilot > Enable MCP servers in Copilot. Suggested URL (https://github.com/settings/copilot/features) or https://github.com/organizations/{organization-name}/settings/copilot/features (This can only be done by their organization admins / owners) +- If none of the above steps work, suggest the user to contact Codacy support + +## Trying to call a tool that needs a rootPath as a parameter +- Always use the standard, non-URL-encoded file system path + +## CRITICAL: Dependencies and Security Checks +- IMMEDIATELY after ANY of these actions: + - Running npm/yarn/pnpm install + - Adding dependencies to package.json + - Adding requirements to requirements.txt + - Adding dependencies to pom.xml + - Adding dependencies to build.gradle + - Any other package manager operations +- You MUST run the `codacy_cli_analyze` tool with: + - `rootPath`: set to the workspace path + - `tool`: set to "trivy" + - `file`: leave empty or unset +- If any vulnerabilities are found because of the newly added packages: + - Stop all other operations + - Propose and apply fixes for the security issues + - Only continue with the original task after security issues are resolved +- EXAMPLE: + - After: npm install react-markdown + - Do: Run codacy_cli_analyze with trivy + - Before: Continuing with any other tasks + +## General +- Repeat the relevant steps for each modified file. +- "Propose fixes" means to both suggest and, if possible, automatically apply the fixes. +- You MUST NOT wait for the user to ask for analysis or remind you to run the tool. +- Do not run `codacy_cli_analyze` looking for changes in duplicated code or code complexity metrics. +- Do not run `codacy_cli_analyze` looking for changes in code coverage. +- Do not try to manually install Codacy CLI using either brew, npm, npx, or any other package manager. +- If the Codacy CLI is not installed, just run the `codacy_cli_analyze` tool from Codacy's MCP Server. +- When calling `codacy_cli_analyze`, only send provider, organization and repository if the project is a git repository. + +## Whenever a call to a Codacy tool that uses `repository` or `organization` as a parameter returns a 404 error +- Offer to run the `codacy_setup_repository` tool to add the repository to Codacy +- If the user accepts, run the `codacy_setup_repository` tool +- Do not ever try to run the `codacy_setup_repository` tool on your own +- After setup, immediately retry the action that failed (only retry once) +--- \ No newline at end of file From 0f962d6b8590a145cd5cd80b7373ef5ec5fa3661 Mon Sep 17 00:00:00 2001 From: Dharmesh Date: Wed, 3 Sep 2025 16:16:53 +0530 Subject: [PATCH 7/8] formatting --- .codacy.yml | 1 + .../firebase-microsoft-oauth-background.ts | 6 +- .../lib/firebase-multi-oauth-background.ts | 10 +- .../src/lib/firebase-oauth.service.ts | 8 +- packages/shared/microsoft-oauth/README.md | 7 +- packages/shared/microsoft-oauth/package.json | 2 +- packages/shared/microsoft-oauth/project.json | 2 +- packages/shared/microsoft-oauth/src/index.ts | 2 +- .../src/lib/microsoft-oauth-background.ts | 2 +- .../src/lib/microsoft-oauth.constant.ts | 2 +- .../src/lib/microsoft-oauth.service.ts | 2 +- .../src/lib/microsoft-oauth.types.ts | 2 +- packages/shared/microsoft-oauth/tsconfig.json | 2 +- .../shared/microsoft-oauth/tsconfig.lib.json | 2 +- .../shared/microsoft-oauth/tsconfig.spec.json | 2 +- .../content/docs/authentication/examples.mdx | 364 +++++++++--------- .../src/content/docs/authentication/index.mdx | 33 +- .../docs/authentication/microsoft-oauth.mdx | 43 +-- 18 files changed, 247 insertions(+), 245 deletions(-) diff --git a/.codacy.yml b/.codacy.yml index 9e66f759..5dc043df 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -4,3 +4,4 @@ exclude_paths: - 'site/*' - 'build/*' - 'userscripts/*' + - '.github/instructions/**' diff --git a/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts b/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts index fd3c4945..f1e7fabe 100644 --- a/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts +++ b/packages/shared/firebase-oauth/src/lib/firebase-microsoft-oauth-background.ts @@ -20,7 +20,7 @@ export class FirebaseMicrosoftOauth2Background extends MicrosoftOauth2Background if (token) { const provider = new OAuthProvider('microsoft.com'); const credential = OAuthProvider.credential(provider.providerId, { - accessToken: token, + accessToken: token }); if (credential) { const { user } = await signInWithCredential(this.auth, credential); @@ -63,7 +63,7 @@ export class FirebaseMicrosoftOauth2Background extends MicrosoftOauth2Background if (token) { const provider = new OAuthProvider('microsoft.com'); const credential = OAuthProvider.credential(provider.providerId, { - accessToken: token, + accessToken: token }); if (credential) { const { user } = await signInWithCredential(this.auth, credential); @@ -76,4 +76,4 @@ export class FirebaseMicrosoftOauth2Background extends MicrosoftOauth2Background } return user; } -} \ No newline at end of file +} diff --git a/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts b/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts index 8587dcef..7e0b0b02 100644 --- a/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts +++ b/packages/shared/firebase-oauth/src/lib/firebase-multi-oauth-background.ts @@ -60,7 +60,7 @@ export class FirebaseMultiOauth2Background { } const firebaseToken = await this.auth.currentUser?.getIdToken(); const headers = new Headers({ Authorization: `Bearer ${firebaseToken}` }); - + if (!token) { if (provider === FirebaseAuthProvider.GOOGLE) { token = (await this.googleOauth._getAuthToken({ scopes })).token; @@ -92,7 +92,7 @@ export class FirebaseMultiOauth2Background { if (token) { const provider = new OAuthProvider('microsoft.com'); const credential = OAuthProvider.credential(provider.providerId, { - accessToken: token, + accessToken: token }); if (credential) { const { user } = await signInWithCredential(this.auth, credential); @@ -115,7 +115,7 @@ export class FirebaseMultiOauth2Background { } catch { // Continue to try Microsoft } - + try { const microsoftResult = await this.#tryMicrosoftAuth(); if (microsoftResult) return microsoftResult; @@ -143,7 +143,7 @@ export class FirebaseMultiOauth2Background { if (token) { const provider = new OAuthProvider('microsoft.com'); const credential = OAuthProvider.credential(provider.providerId, { - accessToken: token, + accessToken: token }); if (credential) { const { user } = await signInWithCredential(this.auth, credential); @@ -152,4 +152,4 @@ export class FirebaseMultiOauth2Background { } return null; } -} \ No newline at end of file +} diff --git a/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts b/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts index 03ce6126..34bc2c69 100644 --- a/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts +++ b/packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts @@ -9,16 +9,16 @@ export class FirebaseOauthService extends CoreService { } static async login(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE) { - return await this.message, FirebaseLoginResponse>({ - messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, + return await this.message, FirebaseLoginResponse>({ + messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseLogin', message: provider }); } static async logout(provider?: FirebaseAuthProvider) { - return await this.message, void>({ - messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, + return await this.message, void>({ + messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseLogout', message: provider }); diff --git a/packages/shared/microsoft-oauth/README.md b/packages/shared/microsoft-oauth/README.md index 2565e690..674be7e2 100644 --- a/packages/shared/microsoft-oauth/README.md +++ b/packages/shared/microsoft-oauth/README.md @@ -68,10 +68,7 @@ await MicrosoftOauthService.logout([MICROSOFT_SCOPES.PROFILE]); "permissions": ["identity"], "oauth2": { "client_id": "your-microsoft-client-id", - "scopes": [ - "https://graph.microsoft.com/User.Read", - "openid" - ] + "scopes": ["https://graph.microsoft.com/User.Read", "openid"] } } ``` @@ -142,4 +139,4 @@ await firebaseAuth.firebaseLogin(); ## Contributing -This package is part of the Auto Clicker AutoFill monorepo. See the main repository for contribution guidelines. \ No newline at end of file +This package is part of the Auto Clicker AutoFill monorepo. See the main repository for contribution guidelines. diff --git a/packages/shared/microsoft-oauth/package.json b/packages/shared/microsoft-oauth/package.json index 525e4cf8..8086ad28 100644 --- a/packages/shared/microsoft-oauth/package.json +++ b/packages/shared/microsoft-oauth/package.json @@ -24,4 +24,4 @@ "@dhruv-techapps/core-service": "0.0.1", "@dhruv-techapps/shared-notifications": "0.0.1" } -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/project.json b/packages/shared/microsoft-oauth/project.json index 42a0258e..35aa3a02 100644 --- a/packages/shared/microsoft-oauth/project.json +++ b/packages/shared/microsoft-oauth/project.json @@ -16,4 +16,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/src/index.ts b/packages/shared/microsoft-oauth/src/index.ts index 61047a2d..abea8a8d 100644 --- a/packages/shared/microsoft-oauth/src/index.ts +++ b/packages/shared/microsoft-oauth/src/index.ts @@ -3,4 +3,4 @@ export * from './lib/microsoft-oauth-background'; export { RUNTIME_MESSAGE_MICROSOFT_OAUTH } from './lib/microsoft-oauth.constant'; export * from './lib/microsoft-oauth.service'; -export * from './lib/microsoft-oauth.types'; \ No newline at end of file +export * from './lib/microsoft-oauth.types'; diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts index 2bf450b9..445e2f26 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts @@ -98,4 +98,4 @@ export class MicrosoftOauth2Background { NotificationHandler.notify(NOTIFICATIONS_ID, NOTIFICATIONS_TITLE, 'Error while retrieving token'); throw new Error('Error while retrieving token'); } -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts index d5a475d1..b6b56c39 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.constant.ts @@ -1 +1 @@ -export const RUNTIME_MESSAGE_MICROSOFT_OAUTH = 'microsoft-oauth'; \ No newline at end of file +export const RUNTIME_MESSAGE_MICROSOFT_OAUTH = 'microsoft-oauth'; diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts index 5901cfa3..e5fcaeb9 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.service.ts @@ -26,4 +26,4 @@ export class MicrosoftOauthService extends CoreService { static async userInfo() { return await this.message({ messenger: RUNTIME_MESSAGE_MICROSOFT_OAUTH, methodName: 'userInfo' }); } -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts index 93d6dde1..f5e8e0da 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts @@ -17,4 +17,4 @@ export type MicrosoftOauth2RemoveResponse = RESPONSE_CODE.REMOVED; export abstract class MicrosoftOauth2Service { abstract login(): Promise; abstract remove(): Promise; -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/tsconfig.json b/packages/shared/microsoft-oauth/tsconfig.json index 48f27607..e861f66a 100644 --- a/packages/shared/microsoft-oauth/tsconfig.json +++ b/packages/shared/microsoft-oauth/tsconfig.json @@ -19,4 +19,4 @@ "path": "./tsconfig.spec.json" } ] -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/tsconfig.lib.json b/packages/shared/microsoft-oauth/tsconfig.lib.json index 64cf7709..7bd727cd 100644 --- a/packages/shared/microsoft-oauth/tsconfig.lib.json +++ b/packages/shared/microsoft-oauth/tsconfig.lib.json @@ -36,4 +36,4 @@ "src/**/*.test.jsx", "src/**/*.spec.jsx" ] -} \ No newline at end of file +} diff --git a/packages/shared/microsoft-oauth/tsconfig.spec.json b/packages/shared/microsoft-oauth/tsconfig.spec.json index 6ac5b70e..78b25f3e 100644 --- a/packages/shared/microsoft-oauth/tsconfig.spec.json +++ b/packages/shared/microsoft-oauth/tsconfig.spec.json @@ -25,4 +25,4 @@ "path": "./tsconfig.lib.json" } ] -} \ No newline at end of file +} diff --git a/site/src/content/docs/authentication/examples.mdx b/site/src/content/docs/authentication/examples.mdx index 02788ef2..450072ef 100644 --- a/site/src/content/docs/authentication/examples.mdx +++ b/site/src/content/docs/authentication/examples.mdx @@ -15,60 +15,66 @@ This page provides practical examples of implementing authentication in Auto Cli ```typescript // background.ts -import { getAuth } from 'firebase/auth/web-extension'; -import { initializeApp } from 'firebase/app'; -import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; +import { getAuth } from 'firebase/auth/web-extension' +import { initializeApp } from 'firebase/app' +import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth' // Initialize Firebase const firebaseConfig = { - apiKey: "your-api-key", - authDomain: "your-project.firebaseapp.com", + apiKey: 'your-api-key', + authDomain: 'your-project.firebaseapp.com' // ... other config -}; +} -const app = initializeApp(firebaseConfig); -const auth = getAuth(app); +const app = initializeApp(firebaseConfig) +const auth = getAuth(app) // Initialize multi-provider authentication const authHandler = new FirebaseMultiOauth2Background( auth, - 'your-google-client-id', // Google OAuth client ID - 'your-microsoft-client-id' // Microsoft OAuth client ID -); + 'your-google-client-id', // Google OAuth client ID + 'your-microsoft-client-id' // Microsoft OAuth client ID +) // Handle authentication messages from content scripts chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { switch (request.action) { case 'microsoft-login': - authHandler.firebaseLogin(FirebaseAuthProvider.MICROSOFT) - .then(result => sendResponse({ success: true, user: result?.user })) - .catch(error => sendResponse({ success: false, error: error.message })); - return true; - + authHandler + .firebaseLogin(FirebaseAuthProvider.MICROSOFT) + .then((result) => sendResponse({ success: true, user: result?.user })) + .catch((error) => sendResponse({ success: false, error: error.message })) + return true + case 'google-login': - authHandler.firebaseLogin(FirebaseAuthProvider.GOOGLE) - .then(result => sendResponse({ success: true, user: result?.user })) - .catch(error => sendResponse({ success: false, error: error.message })); - return true; - + authHandler + .firebaseLogin(FirebaseAuthProvider.GOOGLE) + .then((result) => sendResponse({ success: true, user: result?.user })) + .catch((error) => sendResponse({ success: false, error: error.message })) + return true + case 'logout': - authHandler.firebaseLogout() + authHandler + .firebaseLogout() .then(() => sendResponse({ success: true })) - .catch(error => sendResponse({ success: false, error: error.message })); - return true; - + .catch((error) => sendResponse({ success: false, error: error.message })) + return true + case 'check-auth': - authHandler.firebaseIsLogin() - .then(result => sendResponse({ - success: true, - isLoggedIn: !!result?.user, - user: result?.user, - role: result?.role - })) - .catch(error => sendResponse({ success: false, error: error.message })); - return true; + authHandler + .firebaseIsLogin() + .then((result) => + sendResponse({ + success: true, + isLoggedIn: !!result?.user, + user: result?.user, + role: result?.role + }) + ) + .catch((error) => sendResponse({ success: false, error: error.message })) + return true } -}); +}) ``` #### Content Script Integration @@ -78,76 +84,76 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { class AuthenticationManager { async loginWithMicrosoft(): Promise { try { - const response = await chrome.runtime.sendMessage({ action: 'microsoft-login' }); + const response = await chrome.runtime.sendMessage({ action: 'microsoft-login' }) if (response.success) { - console.log('Microsoft login successful:', response.user); - return true; + console.log('Microsoft login successful:', response.user) + return true } else { - console.error('Microsoft login failed:', response.error); - return false; + console.error('Microsoft login failed:', response.error) + return false } } catch (error) { - console.error('Error during Microsoft login:', error); - return false; + console.error('Error during Microsoft login:', error) + return false } } async loginWithGoogle(): Promise { try { - const response = await chrome.runtime.sendMessage({ action: 'google-login' }); + const response = await chrome.runtime.sendMessage({ action: 'google-login' }) if (response.success) { - console.log('Google login successful:', response.user); - return true; + console.log('Google login successful:', response.user) + return true } else { - console.error('Google login failed:', response.error); - return false; + console.error('Google login failed:', response.error) + return false } } catch (error) { - console.error('Error during Google login:', error); - return false; + console.error('Error during Google login:', error) + return false } } async logout(): Promise { try { - const response = await chrome.runtime.sendMessage({ action: 'logout' }); - return response.success; + const response = await chrome.runtime.sendMessage({ action: 'logout' }) + return response.success } catch (error) { - console.error('Error during logout:', error); - return false; + console.error('Error during logout:', error) + return false } } async checkAuthStatus(): Promise<{ isLoggedIn: boolean; user?: any; role?: string }> { try { - const response = await chrome.runtime.sendMessage({ action: 'check-auth' }); + const response = await chrome.runtime.sendMessage({ action: 'check-auth' }) if (response.success) { return { isLoggedIn: response.isLoggedIn, user: response.user, role: response.role - }; + } } } catch (error) { - console.error('Error checking auth status:', error); + console.error('Error checking auth status:', error) } - return { isLoggedIn: false }; + return { isLoggedIn: false } } } // Usage in content script -const authManager = new AuthenticationManager(); +const authManager = new AuthenticationManager() // Check authentication status on page load -authManager.checkAuthStatus().then(status => { +authManager.checkAuthStatus().then((status) => { if (status.isLoggedIn) { - console.log('User is logged in:', status.user); + console.log('User is logged in:', status.user) // Initialize authenticated features } else { - console.log('User is not logged in'); + console.log('User is not logged in') // Show login options } -}); +}) ``` ### Advanced Examples @@ -156,50 +162,50 @@ authManager.checkAuthStatus().then(status => { ```typescript // separate-providers.ts -import { +import { FirebaseOauth2Background, FirebaseMicrosoftOauth2Background, - FirebaseAuthProvider -} from '@dhruv-techapps/shared-firebase-oauth'; + FirebaseAuthProvider +} from '@dhruv-techapps/shared-firebase-oauth' class ProviderSpecificAuth { - private googleAuth: FirebaseOauth2Background; - private microsoftAuth: FirebaseMicrosoftOauth2Background; + private googleAuth: FirebaseOauth2Background + private microsoftAuth: FirebaseMicrosoftOauth2Background constructor(auth: any) { - this.googleAuth = new FirebaseOauth2Background(auth, 'google-client-id'); - this.microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'microsoft-client-id'); + this.googleAuth = new FirebaseOauth2Background(auth, 'google-client-id') + this.microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'microsoft-client-id') } async loginWithPreferredProvider(provider: 'google' | 'microsoft') { try { - let result; - + let result + if (provider === 'google') { - result = await this.googleAuth.firebaseLogin(); - console.log('Google authentication result:', result); + result = await this.googleAuth.firebaseLogin() + console.log('Google authentication result:', result) } else { - result = await this.microsoftAuth.firebaseLogin(); - console.log('Microsoft authentication result:', result); + result = await this.microsoftAuth.firebaseLogin() + console.log('Microsoft authentication result:', result) } - return result; + return result } catch (error) { - console.error(`${provider} authentication failed:`, error); - throw error; + console.error(`${provider} authentication failed:`, error) + throw error } } async getAuthHeaders(provider: 'google' | 'microsoft', scopes?: string[]) { try { if (provider === 'google') { - return await this.googleAuth._getFirebaseHeaders(scopes); + return await this.googleAuth._getFirebaseHeaders(scopes) } else { - return await this.microsoftAuth._getFirebaseHeaders(scopes); + return await this.microsoftAuth._getFirebaseHeaders(scopes) } } catch (error) { - console.error(`Failed to get ${provider} auth headers:`, error); - throw error; + console.error(`Failed to get ${provider} auth headers:`, error) + throw error } } } @@ -209,76 +215,70 @@ class ProviderSpecificAuth { ```typescript // service-layer-example.ts -import { - FirebaseOauthService, - GoogleOauthService, +import { + FirebaseOauthService, + GoogleOauthService, MicrosoftOauthService, FirebaseAuthProvider, GOOGLE_SCOPES, MICROSOFT_SCOPES -} from '@dhruv-techapps/shared-firebase-oauth'; +} from '@dhruv-techapps/shared-firebase-oauth' class AuthService { // Firebase authentication with provider choice static async authenticateWithFirebase(provider: FirebaseAuthProvider) { try { - const result = await FirebaseOauthService.login(provider); - console.log('Firebase authentication successful:', result); - return result; + const result = await FirebaseOauthService.login(provider) + console.log('Firebase authentication successful:', result) + return result } catch (error) { - console.error('Firebase authentication failed:', error); - throw error; + console.error('Firebase authentication failed:', error) + throw error } } // Direct Google OAuth (without Firebase) static async authenticateWithGoogle() { try { - const result = await GoogleOauthService.login([ - GOOGLE_SCOPES.PROFILE, - GOOGLE_SCOPES.EMAIL - ]); - console.log('Google OAuth successful:', result); - return result; + const result = await GoogleOauthService.login([GOOGLE_SCOPES.PROFILE, GOOGLE_SCOPES.EMAIL]) + console.log('Google OAuth successful:', result) + return result } catch (error) { - console.error('Google OAuth failed:', error); - throw error; + console.error('Google OAuth failed:', error) + throw error } } // Direct Microsoft OAuth (without Firebase) static async authenticateWithMicrosoft() { try { - const result = await MicrosoftOauthService.login([ - MICROSOFT_SCOPES.PROFILE, - MICROSOFT_SCOPES.EMAIL - ]); - console.log('Microsoft OAuth successful:', result); - return result; + const result = await MicrosoftOauthService.login([MICROSOFT_SCOPES.PROFILE, MICROSOFT_SCOPES.EMAIL]) + console.log('Microsoft OAuth successful:', result) + return result } catch (error) { - console.error('Microsoft OAuth failed:', error); - throw error; + console.error('Microsoft OAuth failed:', error) + throw error } } // Check authentication status static async checkStatus() { try { - return await FirebaseOauthService.isLogin(); + return await FirebaseOauthService.isLogin() } catch (error) { - console.error('Status check failed:', error); - return null; + console.error('Status check failed:', error) + return null } } // Logout from all providers static async logout() { try { - await FirebaseOauthService.logout(); - console.log('Logout successful'); + await FirebaseOauthService.logout() + console.log('Logout successful') } catch (error) { - console.error('Logout failed:', error); - throw error; + console.error('Logout failed:', error) + throw error } } } @@ -289,34 +289,34 @@ class AuthService { ```typescript // ui-integration.ts class AuthenticationUI { - private authStatus: { isLoggedIn: boolean; provider?: string; user?: any } = { isLoggedIn: false }; + private authStatus: { isLoggedIn: boolean; provider?: string; user?: any } = { isLoggedIn: false } async initializeUI() { // Check current authentication status - await this.updateAuthStatus(); - + await this.updateAuthStatus() + // Render appropriate UI - this.renderAuthUI(); - + this.renderAuthUI() + // Set up event listeners - this.setupEventListeners(); + this.setupEventListeners() } private async updateAuthStatus() { try { - const response = await chrome.runtime.sendMessage({ action: 'check-auth' }); + const response = await chrome.runtime.sendMessage({ action: 'check-auth' }) if (response.success && response.isLoggedIn) { this.authStatus = { isLoggedIn: true, user: response.user, provider: this.detectProvider(response.user) - }; + } } else { - this.authStatus = { isLoggedIn: false }; + this.authStatus = { isLoggedIn: false } } } catch (error) { - console.error('Failed to check auth status:', error); - this.authStatus = { isLoggedIn: false }; + console.error('Failed to check auth status:', error) + this.authStatus = { isLoggedIn: false } } } @@ -324,16 +324,16 @@ class AuthenticationUI { // Detect provider based on user data if (user?.providerData) { for (const provider of user.providerData) { - if (provider.providerId === 'google.com') return 'Google'; - if (provider.providerId === 'microsoft.com') return 'Microsoft'; + if (provider.providerId === 'google.com') return 'Google' + if (provider.providerId === 'microsoft.com') return 'Microsoft' } } - return 'Unknown'; + return 'Unknown' } private renderAuthUI() { - const container = document.getElementById('auth-container'); - if (!container) return; + const container = document.getElementById('auth-container') + if (!container) return if (this.authStatus.isLoggedIn) { container.innerHTML = ` @@ -342,7 +342,7 @@ class AuthenticationUI {

Welcome, ${this.authStatus.user?.displayName || 'User'}

- `; + ` } else { container.innerHTML = `
@@ -350,65 +350,65 @@ class AuthenticationUI {
- `; + ` } } private setupEventListeners() { document.addEventListener('click', async (event) => { - const target = event.target as HTMLElement; - + const target = event.target as HTMLElement + if (target.id === 'google-login-btn') { - await this.handleLogin('google'); + await this.handleLogin('google') } else if (target.id === 'microsoft-login-btn') { - await this.handleLogin('microsoft'); + await this.handleLogin('microsoft') } else if (target.id === 'logout-btn') { - await this.handleLogout(); + await this.handleLogout() } - }); + }) } private async handleLogin(provider: 'google' | 'microsoft') { try { - const action = provider === 'google' ? 'google-login' : 'microsoft-login'; - const response = await chrome.runtime.sendMessage({ action }); - + const action = provider === 'google' ? 'google-login' : 'microsoft-login' + const response = await chrome.runtime.sendMessage({ action }) + if (response.success) { - await this.updateAuthStatus(); - this.renderAuthUI(); - console.log(`${provider} login successful`); + await this.updateAuthStatus() + this.renderAuthUI() + console.log(`${provider} login successful`) } else { - console.error(`${provider} login failed:`, response.error); - alert(`Login failed: ${response.error}`); + console.error(`${provider} login failed:`, response.error) + alert(`Login failed: ${response.error}`) } } catch (error) { - console.error(`Error during ${provider} login:`, error); - alert(`Login error: ${error.message}`); + console.error(`Error during ${provider} login:`, error) + alert(`Login error: ${error.message}`) } } private async handleLogout() { try { - const response = await chrome.runtime.sendMessage({ action: 'logout' }); - + const response = await chrome.runtime.sendMessage({ action: 'logout' }) + if (response.success) { - this.authStatus = { isLoggedIn: false }; - this.renderAuthUI(); - console.log('Logout successful'); + this.authStatus = { isLoggedIn: false } + this.renderAuthUI() + console.log('Logout successful') } else { - console.error('Logout failed:', response.error); + console.error('Logout failed:', response.error) } } catch (error) { - console.error('Error during logout:', error); + console.error('Error during logout:', error) } } } // Initialize UI when page loads document.addEventListener('DOMContentLoaded', () => { - const authUI = new AuthenticationUI(); - authUI.initializeUI(); -}); + const authUI = new AuthenticationUI() + authUI.initializeUI() +}) ``` ### Error Handling Examples @@ -421,40 +421,40 @@ class AuthErrorHandler { switch (error.message) { case 'Microsoft client id not found': case 'Google client id not found': - return `${provider} authentication is not configured. Please contact support.`; - + return `${provider} authentication is not configured. Please contact support.` + case 'User not logged in': - return 'Please sign in to continue.'; - + return 'Please sign in to continue.' + case 'Invalid Credentials': case 'invalid authentication credentials': - return 'Your credentials have expired. Please sign in again.'; - + return 'Your credentials have expired. Please sign in again.' + case 'Token not found': - return 'Authentication failed. Please try again.'; - + return 'Authentication failed. Please try again.' + case 'Error while retrieving token': - return 'Network error during authentication. Please check your connection.'; - + return 'Network error during authentication. Please check your connection.' + default: - return `Authentication error: ${error.message}`; + return `Authentication error: ${error.message}` } } - - return 'An unknown authentication error occurred.'; + + return 'An unknown authentication error occurred.' } static async withErrorHandling( - operation: () => Promise, + operation: () => Promise, provider: string ): Promise<{ success: boolean; data?: T; error?: string }> { try { - const data = await operation(); - return { success: true, data }; + const data = await operation() + return { success: true, data } } catch (error) { - const errorMessage = this.handleAuthError(error, provider); - console.error(`${provider} operation failed:`, error); - return { success: false, error: errorMessage }; + const errorMessage = this.handleAuthError(error, provider) + console.error(`${provider} operation failed:`, error) + return { success: false, error: errorMessage } } } } @@ -463,15 +463,15 @@ class AuthErrorHandler { const result = await AuthErrorHandler.withErrorHandling( () => FirebaseOauthService.login(FirebaseAuthProvider.MICROSOFT), 'Microsoft' -); +) if (result.success) { - console.log('Authentication successful:', result.data); + console.log('Authentication successful:', result.data) } else { - console.error('Authentication failed:', result.error); + console.error('Authentication failed:', result.error) // Show user-friendly error message - showNotification(result.error); + showNotification(result.error) } ``` -These examples demonstrate comprehensive integration of Microsoft and Google OAuth authentication in Auto Clicker AutoFill extensions, including error handling, UI integration, and service layer usage. \ No newline at end of file +These examples demonstrate comprehensive integration of Microsoft and Google OAuth authentication in Auto Clicker AutoFill extensions, including error handling, UI integration, and service layer usage. diff --git a/site/src/content/docs/authentication/index.mdx b/site/src/content/docs/authentication/index.mdx index f2f6b33c..28c6515d 100644 --- a/site/src/content/docs/authentication/index.mdx +++ b/site/src/content/docs/authentication/index.mdx @@ -12,11 +12,13 @@ Auto Clicker AutoFill supports multiple authentication providers through Firebas ### Supported Providers #### Google OAuth + - **Primary provider** for Google services integration - Supports Google Sheets, Google Drive, and other Google APIs - Established and fully integrated authentication flow #### Microsoft OAuth + - **New support** for Microsoft account authentication - Enables integration with Microsoft Graph APIs - Uses same Firebase Authentication infrastructure @@ -33,6 +35,7 @@ The authentication system is built on Firebase Authentication with OAuth provide ### Implementation Layers #### Background Services + - `GoogleOauth2Background`: Google OAuth implementation - `MicrosoftOauth2Background`: Microsoft OAuth implementation - `FirebaseOauth2Background`: Firebase integration for Google @@ -40,11 +43,13 @@ The authentication system is built on Firebase Authentication with OAuth provide - `FirebaseMultiOauth2Background`: Unified multi-provider support #### Service Layer + - `GoogleOauthService`: Google OAuth messaging service - `MicrosoftOauthService`: Microsoft OAuth messaging service - `FirebaseOauthService`: Firebase authentication messaging service #### Types and Constants + - Standardized interfaces for authentication responses - Provider enums and constants for consistent usage - Error handling types and messaging @@ -52,41 +57,41 @@ The authentication system is built on Firebase Authentication with OAuth provide ### Usage Patterns #### Single Provider Authentication + Use specific provider implementations for dedicated authentication: ```typescript // Google authentication -const googleAuth = new FirebaseOauth2Background(auth, googleClientId); -await googleAuth.firebaseLogin(); +const googleAuth = new FirebaseOauth2Background(auth, googleClientId) +await googleAuth.firebaseLogin() -// Microsoft authentication -const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, microsoftClientId); -await microsoftAuth.firebaseLogin(); +// Microsoft authentication +const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, microsoftClientId) +await microsoftAuth.firebaseLogin() ``` #### Multi-Provider Authentication + Use the unified implementation for flexible provider support: ```typescript -const multiAuth = new FirebaseMultiOauth2Background( - auth, - googleClientId, - microsoftClientId -); +const multiAuth = new FirebaseMultiOauth2Background(auth, googleClientId, microsoftClientId) // Choose provider at runtime -await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE); -await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT); +await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE) +await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT) ``` ### Configuration Requirements #### Firebase Setup + 1. Enable desired OAuth providers in Firebase Console 2. Configure provider-specific settings (client IDs, secrets) 3. Set up appropriate redirect URIs for extension context #### Extension Manifest + Update your extension's manifest to include OAuth configuration: ```json @@ -100,6 +105,7 @@ Update your extension's manifest to include OAuth configuration: ``` #### Provider Registration + - **Google**: Configure in Google Cloud Console - **Microsoft**: Register application in Azure Portal @@ -128,8 +134,9 @@ See the following guides for specific implementation details: ### Browser Support Authentication is supported in: + - Chrome extensions (primary target) - Edge extensions (with appropriate configuration) - Other Chromium-based browsers with extension support -The authentication system is designed to work within the constraints of web extension environments while providing secure and reliable authentication flows. \ No newline at end of file +The authentication system is designed to work within the constraints of web extension environments while providing secure and reliable authentication flows. diff --git a/site/src/content/docs/authentication/microsoft-oauth.mdx b/site/src/content/docs/authentication/microsoft-oauth.mdx index ccae827c..38f51be1 100644 --- a/site/src/content/docs/authentication/microsoft-oauth.mdx +++ b/site/src/content/docs/authentication/microsoft-oauth.mdx @@ -33,42 +33,38 @@ The Microsoft OAuth implementation consists of: #### Basic Microsoft Authentication ```typescript -import { FirebaseMicrosoftOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; +import { FirebaseMicrosoftOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth' // Initialize with Firebase Auth and Microsoft client ID -const auth = getAuth(app); -const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'your-microsoft-client-id'); +const auth = getAuth(app) +const microsoftAuth = new FirebaseMicrosoftOauth2Background(auth, 'your-microsoft-client-id') // Sign in with Microsoft -const result = await microsoftAuth.firebaseLogin(true); -console.log('User:', result?.user); -console.log('Role:', result?.role); +const result = await microsoftAuth.firebaseLogin(true) +console.log('User:', result?.user) +console.log('Role:', result?.role) ``` #### Multi-Provider Authentication ```typescript -import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; +import { FirebaseMultiOauth2Background, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth' // Initialize with both Google and Microsoft client IDs -const auth = getAuth(app); -const multiAuth = new FirebaseMultiOauth2Background( - auth, - 'your-google-client-id', - 'your-microsoft-client-id' -); +const auth = getAuth(app) +const multiAuth = new FirebaseMultiOauth2Background(auth, 'your-google-client-id', 'your-microsoft-client-id') // Sign in with Microsoft -await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT); +await multiAuth.firebaseLogin(FirebaseAuthProvider.MICROSOFT) // Sign in with Google -await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE); +await multiAuth.firebaseLogin(FirebaseAuthProvider.GOOGLE) // Check login status -const loginStatus = await multiAuth.firebaseIsLogin(); +const loginStatus = await multiAuth.firebaseIsLogin() // Logout from specific provider -await multiAuth.firebaseLogout(FirebaseAuthProvider.MICROSOFT); +await multiAuth.firebaseLogout(FirebaseAuthProvider.MICROSOFT) ``` ### Configuration @@ -99,7 +95,7 @@ The implementation includes comprehensive error handling: ```typescript try { - const result = await microsoftAuth.firebaseLogin(true); + const result = await microsoftAuth.firebaseLogin(true) } catch (error) { if (error.message === 'Microsoft client id not found') { // Handle missing client ID @@ -114,12 +110,12 @@ try { Use the service layer for messaging between extension contexts: ```typescript -import { FirebaseOauthService, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth'; +import { FirebaseOauthService, FirebaseAuthProvider } from '@dhruv-techapps/shared-firebase-oauth' // Service calls for cross-context communication -await FirebaseOauthService.login(FirebaseAuthProvider.MICROSOFT); -await FirebaseOauthService.logout(FirebaseAuthProvider.MICROSOFT); -const status = await FirebaseOauthService.isLogin(); +await FirebaseOauthService.login(FirebaseAuthProvider.MICROSOFT) +await FirebaseOauthService.logout(FirebaseAuthProvider.MICROSOFT) +const status = await FirebaseOauthService.isLogin() ``` ### Best Practices @@ -133,6 +129,7 @@ const status = await FirebaseOauthService.isLogin(); ### Browser Compatibility Microsoft OAuth is supported in: + - Chrome extensions - Edge extensions (with appropriate client ID configuration) - Other Chromium-based browsers @@ -142,4 +139,4 @@ Microsoft OAuth is supported in: - Microsoft tokens are handled securely through Firebase Authentication - OAuth flow uses secure redirect URIs - Tokens are automatically refreshed by Firebase -- Authentication state is managed consistently across extension contexts \ No newline at end of file +- Authentication state is managed consistently across extension contexts From df393e989542c3aedaac1bb8e0b6451d80dad96c Mon Sep 17 00:00:00 2001 From: Dharmesh Date: Wed, 3 Sep 2025 16:30:08 +0530 Subject: [PATCH 8/8] Refactor Microsoft OAuth scopes to remove PROFILE scope and default to EMAIL only --- .../microsoft-oauth/src/lib/microsoft-oauth-background.ts | 3 +-- .../shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts index 445e2f26..abc68e38 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth-background.ts @@ -1,4 +1,3 @@ -import { BROWSER } from '@dhruv-techapps/core-common'; import { NotificationHandler } from '@dhruv-techapps/shared-notifications'; import { MICROSOFT_SCOPES, MicrosoftOauth2LoginResponse } from './microsoft-oauth.types'; @@ -46,7 +45,7 @@ export class MicrosoftOauth2Background { } async _getAuthToken({ scopes, interactive }: { scopes?: Array; interactive?: boolean }): Promise { - scopes = scopes || [MICROSOFT_SCOPES.PROFILE, MICROSOFT_SCOPES.EMAIL]; + scopes = scopes || [MICROSOFT_SCOPES.EMAIL]; if (!scopes || scopes.length === 0) { throw new Error('No scopes found'); } diff --git a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts index f5e8e0da..b1576e88 100644 --- a/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts +++ b/packages/shared/microsoft-oauth/src/lib/microsoft-oauth.types.ts @@ -1,7 +1,6 @@ import { RESPONSE_CODE } from '@dhruv-techapps/core-common'; export enum MICROSOFT_SCOPES { - PROFILE = 'https://graph.microsoft.com/User.Read', EMAIL = 'https://graph.microsoft.com/User.Read', OPENID = 'openid', OFFLINE_ACCESS = 'offline_access'