Skip to content

Commit 427e83b

Browse files
committed
feat(auth): add support for api keys
1 parent da383ec commit 427e83b

24 files changed

Lines changed: 258 additions & 28 deletions

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
"hooks": {
2020
"pre-commit": "npm run lint"
2121
}
22-
}
22+
},
23+
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
2324
}

products/userale/packages/flagon-userale-ext/src/background/messages/config_change.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
3232
export function setOptions(options: StoredOptions) {
3333
userale.options({ url: options.loggingUrl })
3434
allowListRegExp = new RegExp(options.allowList)
35+
36+
switch (options.authMode) {
37+
case "oauth":
38+
userale.options({
39+
authHeader: options.accessToken ? `Bearer ${options.accessToken}` : null,
40+
apiKey: null
41+
})
42+
break
43+
case "apikey":
44+
userale.options({
45+
authHeader: null,
46+
apiKey: options.apiKey || null
47+
})
48+
break
49+
default:
50+
userale.options({ authHeader: null, apiKey: null })
51+
}
3552
}
3653

3754
export function getAllowListRegExp() {

products/userale/packages/flagon-userale-ext/src/options/auth.tsx

Lines changed: 117 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,47 @@
1818
*/
1919

2020
import pkceChallenge from "pkce-challenge"
21-
import { useState } from "react"
21+
import { useEffect, useState } from "react"
2222
import browser from "webextension-polyfill"
2323

24-
import { setStoredOptions } from "~/utils/storage"
24+
import { getStoredOptions, setStoredOptions } from "~/utils/storage"
25+
import type { StoredOptions } from "~/utils/storage"
2526

2627
function Auth() {
28+
const [authMode, setAuthMode] = useState<StoredOptions["authMode"]>("none")
29+
const [apiKey, setApiKey] = useState("")
2730
const [issuerUrl, setIssuerUrl] = useState("")
2831
const [clientId, setClientId] = useState("")
2932
const [message, setMessage] = useState("")
3033

34+
useEffect(() => {
35+
getStoredOptions().then((opts) => {
36+
setAuthMode(opts.authMode)
37+
setApiKey(opts.apiKey)
38+
})
39+
}, [])
40+
41+
const handleAuthModeChange = async (
42+
mode: StoredOptions["authMode"]
43+
) => {
44+
setAuthMode(mode)
45+
setMessage("")
46+
if (mode === "none") {
47+
await setStoredOptions({ authMode: "none", accessToken: "", apiKey: "" })
48+
setApiKey("")
49+
setMessage("Auth disabled.")
50+
}
51+
}
52+
53+
const handleSaveApiKey = async () => {
54+
await setStoredOptions({
55+
authMode: "apikey",
56+
apiKey,
57+
accessToken: ""
58+
})
59+
setMessage("API key saved.")
60+
}
61+
3162
const handleLogin = async () => {
3263
try {
3364
// Generate the PKCE challenge pair (code_verifier and code_challenge)
@@ -74,7 +105,11 @@ function Auth() {
74105
)
75106

76107
const tokens = await tokenRes.json()
77-
await setStoredOptions({ accessToken: tokens.access_token })
108+
await setStoredOptions({
109+
authMode: "oauth",
110+
accessToken: tokens.access_token,
111+
apiKey: ""
112+
})
78113

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

86121
return (
87122
<div>
88-
<h2>OAuth Login</h2>
89-
<form onSubmit={handleLogin}>
90-
<div>
91-
<label>Issuer URL:</label>
123+
<h2>Authentication</h2>
124+
<div>
125+
<label>
92126
<input
93-
type="url"
94-
value={issuerUrl}
95-
onChange={(e) => setIssuerUrl(e.target.value)}
96-
placeholder="https://issuer.com/realms/myrealm"
97-
required
127+
type="radio"
128+
name="authMode"
129+
value="none"
130+
checked={authMode === "none"}
131+
onChange={() => handleAuthModeChange("none")}
98132
/>
99-
</div>
100-
<div>
101-
<label>Client ID:</label>
133+
None
134+
</label>
135+
<label style={{ marginLeft: 16 }}>
136+
<input
137+
type="radio"
138+
name="authMode"
139+
value="oauth"
140+
checked={authMode === "oauth"}
141+
onChange={() => handleAuthModeChange("oauth")}
142+
/>
143+
OAuth
144+
</label>
145+
<label style={{ marginLeft: 16 }}>
102146
<input
103-
type="text"
104-
value={clientId}
105-
onChange={(e) => setClientId(e.target.value)}
106-
placeholder="your-client-id"
107-
required
147+
type="radio"
148+
name="authMode"
149+
value="apikey"
150+
checked={authMode === "apikey"}
151+
onChange={() => handleAuthModeChange("apikey")}
108152
/>
153+
API Key
154+
</label>
155+
</div>
156+
157+
{authMode === "oauth" && (
158+
<div>
159+
<h3>OAuth Login</h3>
160+
<form onSubmit={handleLogin}>
161+
<div>
162+
<label>Issuer URL:</label>
163+
<input
164+
type="url"
165+
value={issuerUrl}
166+
onChange={(e) => setIssuerUrl(e.target.value)}
167+
placeholder="https://issuer.com/realms/myrealm"
168+
required
169+
/>
170+
</div>
171+
<div>
172+
<label>Client ID:</label>
173+
<input
174+
type="text"
175+
value={clientId}
176+
onChange={(e) => setClientId(e.target.value)}
177+
placeholder="your-client-id"
178+
required
179+
/>
180+
</div>
181+
<div style={{ textAlign: "right" }}>
182+
<button type="submit">Log In</button>
183+
</div>
184+
</form>
109185
</div>
110-
<div style={{ textAlign: "right" }}>
111-
<button type="submit">Log In</button>
186+
)}
187+
188+
{authMode === "apikey" && (
189+
<div>
190+
<h3>API Key</h3>
191+
<div>
192+
<label>API Key:</label>
193+
<input
194+
type="password"
195+
value={apiKey}
196+
onChange={(e) => setApiKey(e.target.value)}
197+
placeholder="Enter your API key"
198+
/>
199+
</div>
200+
<div style={{ textAlign: "right" }}>
201+
<button type="button" onClick={handleSaveApiKey}>
202+
Save
203+
</button>
204+
</div>
112205
</div>
113-
</form>
206+
)}
207+
114208
{message && <p>{message}</p>}
115209
</div>
116210
)

products/userale/packages/flagon-userale-ext/src/utils/storage.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { sendToBackground } from "@plasmohq/messaging"
2323

2424
export const STORAGE_KEYS = {
2525
accessToken: "accessToken",
26+
apiKey: "apiKey",
27+
authMode: "authMode",
2628
allowList: "allowList",
2729
loggingUrl: "loggingUrl"
2830
} as const
@@ -31,12 +33,16 @@ export type StorageKeys = keyof typeof STORAGE_KEYS
3133

3234
export type StoredOptions = {
3335
accessToken: string
36+
apiKey: string
37+
authMode: "oauth" | "apikey" | "none"
3438
allowList: string
3539
loggingUrl: string
3640
}
3741

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

4955
return {
5056
accessToken: stored.accessToken ?? DEFAULT_OPTIONS.accessToken,
57+
apiKey: stored.apiKey ?? DEFAULT_OPTIONS.apiKey,
58+
authMode: stored.authMode ?? DEFAULT_OPTIONS.authMode,
5159
allowList: stored.allowList ?? DEFAULT_OPTIONS.allowList,
5260
loggingUrl: stored.loggingUrl ?? DEFAULT_OPTIONS.loggingUrl
5361
}
@@ -57,8 +65,8 @@ export async function getStoredOptions(): Promise<StoredOptions> {
5765
export async function setStoredOptions(values: Partial<StoredOptions>) {
5866
// Validate the new options
5967
try {
60-
new RegExp(values.allowList)
61-
new URL(values.loggingUrl)
68+
if (values.allowList !== undefined) new RegExp(values.allowList)
69+
if (values.loggingUrl !== undefined) new URL(values.loggingUrl)
6270
} catch (error) {
6371
return error
6472
}

products/userale/packages/flagon-userale/build/attachHandlers.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

products/userale/packages/flagon-userale/build/configure.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type { Settings } from "@/types";
1818
export declare class Configuration {
1919
[key: string]: Settings.ConfigValueTypes;
2020
private static instance;
21+
apiKey: Settings.ApiKey;
2122
autostart: boolean;
2223
authHeader: Settings.AuthHeader;
2324
browserSessionId: Settings.SessionId;

products/userale/packages/flagon-userale/build/configure.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

products/userale/packages/flagon-userale/build/getInitialSettings.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

products/userale/packages/flagon-userale/build/main.d.ts.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

products/userale/packages/flagon-userale/build/main.global.js

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)