Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions .codacy.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
exclude_paths:
- 'README.md'
- '**/**/eslint.config.mjs'
- '**/**/package.json'
- '**/**/tsconfig.json'
- '**/**/vite.config.ts'
- '**/**/project.json'
- '**/**/tsconfig.*.json'
- '**/**/webpack.config.js'
- 'scripts/*'
- 'site/*'
- 'build/*'
- 'userscripts/*'
- '.github/instructions/**'
65 changes: 65 additions & 0 deletions .github/instructions/codacy.instructions.md
Original file line number Diff line number Diff line change
@@ -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)
---
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/shared/firebase-oauth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/firebase-oauth/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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<FirebaseLoginResponse> {
return await this.auth.authStateReady().then(async () => {
return await this.#getUserAndRole(this.auth.currentUser);
});
}

async firebaseLogin(interactive = true): Promise<FirebaseLoginResponse> {
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<FirebaseLoginResponse> {
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;
}
}
Original file line number Diff line number Diff line change
@@ -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<FirebaseLoginResponse> {
return await this.auth.authStateReady().then(async () => {
return await this.#getUserAndRole(this.auth.currentUser);
});
}

async firebaseLogin(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE, interactive = true): Promise<FirebaseLoginResponse> {
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<FirebaseLoginResponse> {
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<FirebaseLoginResponse> {
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<FirebaseLoginResponse> {
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<FirebaseLoginResponse | null> {
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<FirebaseLoginResponse | null> {
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;
}
}
18 changes: 13 additions & 5 deletions packages/shared/firebase-oauth/src/lib/firebase-oauth.service.ts
Original file line number Diff line number Diff line change
@@ -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<RuntimeMessageRequest, FirebaseLoginResponse>({ messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseIsLogin' });
}

static async login() {
return await this.message<RuntimeMessageRequest, FirebaseLoginResponse>({ messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH, methodName: 'firebaseLogin' });
static async login(provider: FirebaseAuthProvider = FirebaseAuthProvider.GOOGLE) {
return await this.message<RuntimeMessageRequest<FirebaseAuthProvider>, 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<RuntimeMessageRequest<FirebaseAuthProvider | undefined>, void>({
messenger: RUNTIME_MESSAGE_FIREBASE_OAUTH,
methodName: 'firebaseLogout',
message: provider
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/firebase-oauth/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
{
"path": "../google-oauth"
},
{
"path": "../microsoft-oauth"
},
{
"path": "./tsconfig.lib.json"
},
Expand Down
Loading
Loading