-
Notifications
You must be signed in to change notification settings - Fork 5
feat: FI-49 Add Mixpanel basic auth #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: CMS
Are you sure you want to change the base?
Changes from all commits
33d3da2
95d10eb
e3f2fa0
c31bfd5
c293336
1feaae4
4666f07
1be948b
f4ac452
c2c8fd1
79474ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,15 @@ | ||
| { | ||
| "Google": ["OAuth2", "APIKey"], | ||
| "Facebook": ["OAuth2"] | ||
| } | ||
| "Google": [ | ||
| "OAuth2", | ||
| "APIKey" | ||
| ], | ||
| "Facebook": [ | ||
| "OAuth2" | ||
| ], | ||
| "GitHub": [ | ||
| "OAuth2" | ||
| ], | ||
| "Mixpanel": [ | ||
| "Basic" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { type BasicAuthString } from "~/external-auth-integration/auth-providers/basic-auth/@types"; | ||
|
|
||
| export class BasicAuthVerifyDto { | ||
| providerKey: BasicAuthString; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { type BasicAuthString } from "~/external-auth-integration/auth-providers/basic-auth/@types"; | ||
|
|
||
| export class BasicAuthDto { | ||
| providerKey: BasicAuthString; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { type OAuthString } from "../auth-providers/oauth2/@types"; | ||
| import { type BasicAuthString } from "../auth-providers/basic-auth/@types"; | ||
| import { IOAuth } from "../auth-providers/oauth2/interface/ioauth.interface"; | ||
| import { IBasicAuth } from "../auth-providers/basic-auth/interface/basic-auth.interface"; | ||
|
|
||
| export type AuthString = OAuthString | BasicAuthString; | ||
| export type AuthService = IOAuth | IBasicAuth; |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to change comments
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of this draft PR is to check if the general idea of the implementation of basic auth is correct; all the comments and docs were pasted over from existing files and are all wrong and should be ignored. I'll add docs and tests and linting and formatting after getting confirmation that the general idea is right... |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| export type BasicAuthString = `basic-${string}`; | ||
|
|
||
| export interface BasicAuthProvider { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IBasicAuthConfiguration |
||
| authenticateUrl: string; | ||
| verifyUrl?: string; | ||
| } | ||
|
|
||
| export interface BasicAuthConfig { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found that name in OAuth may be confused, actually BasicAuthConfig cannot store pw and username
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pw and username are the service account username and secret; I thought that's the credential we're supposed to put in .env (like the clientId and client secret of OAuth)? The authenticate() method basically just verifies it, and the verify() method retrieves an expiration date (generally service accounts don't expire so it would be
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we will store PW and username in db, actually in OAuth 2, we still store credentials in db, however the interface name ICredential in OAuth is confused, I have made a new PR to change it |
||
| provider: BasicAuthProvider; | ||
| } | ||
|
|
||
| export interface BasicAuthVerificationResponse { | ||
| isValid: boolean; | ||
| expiresIn?: number; | ||
| // Other fields as per your application's requirements | ||
| [key: string]: any; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { Module } from "@nestjs/common"; | ||
| import { HttpModule } from "@nestjs/axios"; | ||
| import { EncryptionDecryptionModule } from "~/encryption-decryption/encryption-decryption.module"; | ||
| import { MixpanelV1BasicAuthService } from "./mixpanel/v1/mixpanel.v1.service"; | ||
| import { MixpanelV1BasicAuthConfig } from "./mixpanel/v1/mixpanel.v1.config"; | ||
|
|
||
| @Module({ | ||
| imports: [HttpModule, EncryptionDecryptionModule], | ||
| providers: [ | ||
| MixpanelV1BasicAuthService, | ||
| { | ||
| provide: "MixpanelV1BasicAuthConfig", | ||
| useFactory: () => new MixpanelV1BasicAuthConfig().basicAuthConfig, | ||
| }, | ||
| ], | ||
| exports: [MixpanelV1BasicAuthService], | ||
| }) | ||
| export class BasicAuthModule {} |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change comment |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export interface IBasicAuth { | ||
| authenticate(username: string, password: string): Promise<any>; | ||
|
|
||
| verify?(username: string, password: string): Promise<any>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { Injectable } from "@nestjs/common"; | ||
| import { BasicAuthConfig } from "../../@types"; | ||
|
|
||
| /** | ||
| * Service for configuring Mixpanel V1 Basic HTTP Authentication. | ||
| */ | ||
| @Injectable() | ||
| export class MixpanelV1BasicAuthConfig { | ||
| public basicAuthConfig: BasicAuthConfig; | ||
|
|
||
| constructor() { | ||
| this.basicAuthConfig = { | ||
| provider: { | ||
| authenticateUrl: "https://mixpanel.com/api/app/me", | ||
| verifyUrl: "https://mixpanel.com/api/app/organizations/{organizationId}/service-accounts", | ||
faze059 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { Injectable, Inject, Logger } from "@nestjs/common"; | ||
| import { HttpService } from "@nestjs/axios"; | ||
| import { EncryptionDecryptionService } from "~/encryption-decryption/encryption-decryption.service"; | ||
| import { lastValueFrom } from "rxjs"; | ||
| import { map } from "rxjs/operators"; | ||
|
|
||
| import { IBasicAuth } from "../../interface/basic-auth.interface"; | ||
| import { BasicAuthConfig, BasicAuthVerificationResponse } from "../../@types"; | ||
|
|
||
| /** | ||
| * Service to handle Mixpanel V1 Basic HTTP Authentication. | ||
| */ | ||
| @Injectable() | ||
| export class MixpanelV1BasicAuthService implements IBasicAuth { | ||
| constructor( | ||
| @Inject("MixpanelV1BasicAuthConfig") private readonly config: BasicAuthConfig, | ||
| private readonly httpService: HttpService, | ||
| private readonly encryptionDecryptionService: EncryptionDecryptionService, | ||
| ) {} | ||
|
|
||
| private readonly logger = new Logger(MixpanelV1BasicAuthService.name); | ||
|
|
||
| async authenticate(username: string, password: string): Promise<object> { | ||
| try { | ||
| const { authenticateUrl } = this.config.provider; | ||
| const data = await lastValueFrom( | ||
| this.httpService.get(authenticateUrl, { auth: { username, password } }).pipe(map((resp) => resp.data)), | ||
| ); | ||
|
|
||
| return { | ||
| status: data.status, | ||
| organizationId: Object.keys(data.results?.organizations).at(0), | ||
| }; | ||
| } catch (error) { | ||
| this.logger.error(`Authentication failed: ${error}`); | ||
| const data = error.response?.data; | ||
| return data; | ||
| } | ||
| } | ||
|
|
||
| async verify(username: string, password: string): Promise<BasicAuthVerificationResponse> { | ||
| try { | ||
| const authenticationResponse = await this.authenticate(username, password); | ||
| if (!authenticationResponse["organizationId"]) { | ||
| throw new Error("Authentication failed"); | ||
| } | ||
|
|
||
| let { verifyUrl } = this.config.provider; | ||
| if (!verifyUrl) { | ||
| throw new Error("verifyUrl not found"); | ||
| } | ||
|
|
||
| verifyUrl = verifyUrl.replace("{organizationId}", authenticationResponse["organizationId"]); | ||
| const data = await lastValueFrom( | ||
| this.httpService.get(verifyUrl, { auth: { username, password } }).pipe(map((resp) => resp.data)), | ||
| ); | ||
|
|
||
| return { | ||
| isValid: true, | ||
| expiresIn: data.results?.expires, | ||
| }; | ||
| } catch (error) { | ||
| this.logger.error(`Service account verification failed: ${error}`); | ||
| return { isValid: false }; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.