Skip to content
Open
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: 2 additions & 6 deletions .github/workflows/userale_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Setup pnpm v10
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Install dependencies
run: pnpm i
Expand All @@ -80,10 +78,8 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Setup pnpm v10
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Install dependencies
run: pnpm i
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
"hooks": {
"pre-commit": "npm run lint"
}
}
},
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,30 @@

let allowListRegExp: RegExp

const handler: PlasmoMessaging.MessageHandler = async (req, res) => {

Check warning on line 28 in products/userale/packages/flagon-userale-ext/src/background/messages/config_change.ts

View workflow job for this annotation

GitHub Actions / pre-build (18.x)

'res' is defined but never used

Check warning on line 28 in products/userale/packages/flagon-userale-ext/src/background/messages/config_change.ts

View workflow job for this annotation

GitHub Actions / pre-build (18.x)

'res' is defined but never used
setOptions(req.body)
}

export function setOptions(options: StoredOptions) {
userale.options({ url: options.loggingUrl })
allowListRegExp = new RegExp(options.allowList)

switch (options.authMode) {
case "oauth":
userale.options({
authHeader: options.accessToken ? `Bearer ${options.accessToken}` : null,
apiKey: null
})
break
case "apikey":
userale.options({
authHeader: null,
apiKey: options.apiKey || null
})
break
default:
userale.options({ authHeader: null, apiKey: null })
}
}

export function getAllowListRegExp() {
Expand Down
140 changes: 117 additions & 23 deletions products/userale/packages/flagon-userale-ext/src/options/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,47 @@
*/

import pkceChallenge from "pkce-challenge"
import { useState } from "react"
import { useEffect, useState } from "react"
import browser from "webextension-polyfill"

import { setStoredOptions } from "~/utils/storage"
import { getStoredOptions, setStoredOptions } from "~/utils/storage"
import type { StoredOptions } from "~/utils/storage"

function Auth() {
const [authMode, setAuthMode] = useState<StoredOptions["authMode"]>("none")
const [apiKey, setApiKey] = useState("")
const [issuerUrl, setIssuerUrl] = useState("")
const [clientId, setClientId] = useState("")
const [message, setMessage] = useState("")

useEffect(() => {
getStoredOptions().then((opts) => {
setAuthMode(opts.authMode)
setApiKey(opts.apiKey)
})
}, [])

const handleAuthModeChange = async (
mode: StoredOptions["authMode"]
) => {
setAuthMode(mode)
setMessage("")
if (mode === "none") {
await setStoredOptions({ authMode: "none", accessToken: "", apiKey: "" })
setApiKey("")
setMessage("Auth disabled.")
}
}

const handleSaveApiKey = async () => {
await setStoredOptions({
authMode: "apikey",
apiKey,
accessToken: ""
})
setMessage("API key saved.")
}

const handleLogin = async () => {
try {
// Generate the PKCE challenge pair (code_verifier and code_challenge)
Expand Down Expand Up @@ -74,7 +105,11 @@ function Auth() {
)

const tokens = await tokenRes.json()
await setStoredOptions({ accessToken: tokens.access_token })
await setStoredOptions({
authMode: "oauth",
accessToken: tokens.access_token,
apiKey: ""
})

setMessage("Login successful!")
} catch (err) {
Expand All @@ -85,32 +120,91 @@ function Auth() {

return (
<div>
<h2>OAuth Login</h2>
<form onSubmit={handleLogin}>
<div>
<label>Issuer URL:</label>
<h2>Authentication</h2>
<div>
<label>
<input
type="url"
value={issuerUrl}
onChange={(e) => setIssuerUrl(e.target.value)}
placeholder="https://issuer.com/realms/myrealm"
required
type="radio"
name="authMode"
value="none"
checked={authMode === "none"}
onChange={() => handleAuthModeChange("none")}
/>
</div>
<div>
<label>Client ID:</label>
None
</label>
<label style={{ marginLeft: 16 }}>
<input
type="radio"
name="authMode"
value="oauth"
checked={authMode === "oauth"}
onChange={() => handleAuthModeChange("oauth")}
/>
OAuth
</label>
<label style={{ marginLeft: 16 }}>
<input
type="text"
value={clientId}
onChange={(e) => setClientId(e.target.value)}
placeholder="your-client-id"
required
type="radio"
name="authMode"
value="apikey"
checked={authMode === "apikey"}
onChange={() => handleAuthModeChange("apikey")}
/>
API Key
</label>
</div>

{authMode === "oauth" && (
<div>
<h3>OAuth Login</h3>
<form onSubmit={handleLogin}>
<div>
<label>Issuer URL:</label>
<input
type="url"
value={issuerUrl}
onChange={(e) => setIssuerUrl(e.target.value)}
placeholder="https://issuer.com/realms/myrealm"
required
/>
</div>
<div>
<label>Client ID:</label>
<input
type="text"
value={clientId}
onChange={(e) => setClientId(e.target.value)}
placeholder="your-client-id"
required
/>
</div>
<div style={{ textAlign: "right" }}>
<button type="submit">Log In</button>
</div>
</form>
</div>
<div style={{ textAlign: "right" }}>
<button type="submit">Log In</button>
)}

{authMode === "apikey" && (
<div>
<h3>API Key</h3>
<div>
<label>API Key:</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="Enter your API key"
/>
</div>
<div style={{ textAlign: "right" }}>
<button type="button" onClick={handleSaveApiKey}>
Save
</button>
</div>
</div>
</form>
)}

{message && <p>{message}</p>}
</div>
)
Expand Down
12 changes: 10 additions & 2 deletions products/userale/packages/flagon-userale-ext/src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { sendToBackground } from "@plasmohq/messaging"

export const STORAGE_KEYS = {
accessToken: "accessToken",
apiKey: "apiKey",
authMode: "authMode",
allowList: "allowList",
loggingUrl: "loggingUrl"
} as const
Expand All @@ -31,12 +33,16 @@ export type StorageKeys = keyof typeof STORAGE_KEYS

export type StoredOptions = {
accessToken: string
apiKey: string
authMode: "oauth" | "apikey" | "none"
allowList: string
loggingUrl: string
}

const DEFAULT_OPTIONS: StoredOptions = {
accessToken: "",
apiKey: "",
authMode: "none",
allowList: "https://flagon.apache.org/",
loggingUrl: "http://localhost:8000"
}
Expand All @@ -48,6 +54,8 @@ export async function getStoredOptions(): Promise<StoredOptions> {

return {
accessToken: stored.accessToken ?? DEFAULT_OPTIONS.accessToken,
apiKey: stored.apiKey ?? DEFAULT_OPTIONS.apiKey,
authMode: stored.authMode ?? DEFAULT_OPTIONS.authMode,
allowList: stored.allowList ?? DEFAULT_OPTIONS.allowList,
loggingUrl: stored.loggingUrl ?? DEFAULT_OPTIONS.loggingUrl
}
Expand All @@ -57,8 +65,8 @@ export async function getStoredOptions(): Promise<StoredOptions> {
export async function setStoredOptions(values: Partial<StoredOptions>) {
// Validate the new options
try {
new RegExp(values.allowList)
new URL(values.loggingUrl)
if (values.allowList !== undefined) new RegExp(values.allowList)
if (values.loggingUrl !== undefined) new URL(values.loggingUrl)
} catch (error) {
return error
}
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { Settings } from "@/types";
export declare class Configuration {
[key: string]: Settings.ConfigValueTypes;
private static instance;
apiKey: Settings.ApiKey;
autostart: boolean;
authHeader: Settings.AuthHeader;
browserSessionId: Settings.SessionId;
Expand Down

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

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

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

9 changes: 9 additions & 0 deletions products/userale/packages/flagon-userale/build/main.global.js

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

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions products/userale/packages/flagon-userale/build/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ var httpSessionId = null;
function getInitialSettings() {
if (typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope) {
const settings2 = {
apiKey: null,
authHeader: null,
autostart: true,
browserSessionId: null,
Expand Down Expand Up @@ -577,6 +578,7 @@ function getInitialSettings() {
};
const headers = get("data-headers");
const settings = {
apiKey: get("data-api-key") || null,
authHeader: get("data-auth") || null,
autostart: get("data-autostart") === "false" ? false : true,
browserSessionId: null,
Expand Down Expand Up @@ -642,6 +644,7 @@ function generatehttpSessionId() {
// src/configure.ts
var _Configuration = class {
constructor() {
this.apiKey = null;
this.autostart = false;
this.authHeader = null;
this.browserSessionId = null;
Expand Down Expand Up @@ -754,6 +757,9 @@ function sendOnClose(logs3, config3) {
if (config3.authHeader) {
headers.set("Authorization", config3.authHeader.toString());
}
if (config3.apiKey) {
headers.set("x-api-key", config3.apiKey);
}
fetch(config3.url, {
keepalive: true,
method: "POST",
Expand Down Expand Up @@ -782,6 +788,9 @@ async function sendLogs(logs3, config3, retries) {
const authHeaderValue = typeof config3.authHeader === "function" ? config3.authHeader() : config3.authHeader;
headers.set("Authorization", authHeaderValue);
}
if (config3.apiKey) {
headers.set("x-api-key", config3.apiKey);
}
updateCustomHeaders(config3);
if (config3.headers) {
for (const [header, value] of Object.entries(config3.headers)) {
Expand Down

Large diffs are not rendered by default.

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

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

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

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

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

Loading
Loading