bright-auth 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bright-auth.cjs.js
CHANGED
|
@@ -128,12 +128,21 @@ var src_default = BrightAuth;
|
|
|
128
128
|
function registerSignInButton() {
|
|
129
129
|
if (customElements.get("bright-signin")) return;
|
|
130
130
|
customElements.define("bright-signin", class BrightSignIn extends HTMLElement {
|
|
131
|
-
connectedCallback() {
|
|
131
|
+
async connectedCallback() {
|
|
132
132
|
const clientId = this.getAttribute("client-id") || "";
|
|
133
133
|
const clientSecret = this.getAttribute("client-secret") || "";
|
|
134
134
|
const redirectUri = this.getAttribute("redirect-uri") || "";
|
|
135
135
|
const scopes = (this.getAttribute("scopes") || "health").split(",").map((s) => s.trim());
|
|
136
136
|
const auth = new BrightAuth({ clientId, clientSecret, redirectUri });
|
|
137
|
+
try {
|
|
138
|
+
const token = await auth.handleCallback();
|
|
139
|
+
if (token) {
|
|
140
|
+
const user = await auth.getUser().catch(() => null);
|
|
141
|
+
this.dispatchEvent(new CustomEvent("bright-auth", { detail: { token, user }, bubbles: true }));
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
this.dispatchEvent(new CustomEvent("bright-auth-error", { detail: { error: e.message }, bubbles: true }));
|
|
145
|
+
}
|
|
137
146
|
const shadow = this.attachShadow({ mode: "closed" });
|
|
138
147
|
const style = document.createElement("style");
|
|
139
148
|
style.textContent = `
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n async connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n // Automatically handle the OAuth callback if ?code= is in the URL\n try {\n const token = await auth.handleCallback()\n if (token) {\n const user = await auth.getUser().catch(() => null)\n this.dispatchEvent(new CustomEvent(\"bright-auth\", { detail: { token, user }, bubbles: true }))\n }\n } catch (e) {\n this.dispatchEvent(new CustomEvent(\"bright-auth-error\", { detail: { error: e.message }, bubbles: true }))\n }\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/bright-auth.esm.js
CHANGED
|
@@ -103,12 +103,21 @@ var src_default = BrightAuth;
|
|
|
103
103
|
function registerSignInButton() {
|
|
104
104
|
if (customElements.get("bright-signin")) return;
|
|
105
105
|
customElements.define("bright-signin", class BrightSignIn extends HTMLElement {
|
|
106
|
-
connectedCallback() {
|
|
106
|
+
async connectedCallback() {
|
|
107
107
|
const clientId = this.getAttribute("client-id") || "";
|
|
108
108
|
const clientSecret = this.getAttribute("client-secret") || "";
|
|
109
109
|
const redirectUri = this.getAttribute("redirect-uri") || "";
|
|
110
110
|
const scopes = (this.getAttribute("scopes") || "health").split(",").map((s) => s.trim());
|
|
111
111
|
const auth = new BrightAuth({ clientId, clientSecret, redirectUri });
|
|
112
|
+
try {
|
|
113
|
+
const token = await auth.handleCallback();
|
|
114
|
+
if (token) {
|
|
115
|
+
const user = await auth.getUser().catch(() => null);
|
|
116
|
+
this.dispatchEvent(new CustomEvent("bright-auth", { detail: { token, user }, bubbles: true }));
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
this.dispatchEvent(new CustomEvent("bright-auth-error", { detail: { error: e.message }, bubbles: true }));
|
|
120
|
+
}
|
|
112
121
|
const shadow = this.attachShadow({ mode: "closed" });
|
|
113
122
|
const style = document.createElement("style");
|
|
114
123
|
style.textContent = `
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n async connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n // Automatically handle the OAuth callback if ?code= is in the URL\n try {\n const token = await auth.handleCallback()\n if (token) {\n const user = await auth.getUser().catch(() => null)\n this.dispatchEvent(new CustomEvent(\"bright-auth\", { detail: { token, user }, bubbles: true }))\n }\n } catch (e) {\n this.dispatchEvent(new CustomEvent(\"bright-auth-error\", { detail: { error: e.message }, bubbles: true }))\n }\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|