@mentra/react 0.2.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/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # MentraOS React Auth Library (`@mentra/react`)
2
+
3
+ This library simplifies authentication for React-based webviews running within the MentraOS manager application. It handles the extraction and verification of the `aos_signed_user_token` provided by the MentraOS system and makes user information available through a React Context and Hook.
4
+
5
+ Check out the full [MentraOS React Documentation](https://docs.mentra.glass/react-webviews) for more details.
6
+
7
+ ## Prerequisites
8
+
9
+ - React 16.8+
10
+ - Your webview must be opened by the MentraOS manager application, which will append the `aos_signed_user_token` to the URL.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ bun add @mentra/react
16
+ # or
17
+ npm install @mentra/react
18
+ # or
19
+ yarn add @mentra/react
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### 1. Wrap your application with `MentraAuthProvider`
25
+
26
+ In your main application file (e.g., `src/main.tsx` or `src/index.tsx`):
27
+
28
+ ```tsx
29
+ // src/main.tsx
30
+ import React from 'react';
31
+ import ReactDOM from 'react-dom/client';
32
+ import App from './App';
33
+ import { MentraAuthProvider } from '@mentra/react';
34
+
35
+ ReactDOM.createRoot(document.getElementById('root')!).render(
36
+ <React.StrictMode>
37
+ <MentraAuthProvider>
38
+ <App />
39
+ </MentraAuthProvider>
40
+ </React.StrictMode>
41
+ );
42
+ ```
43
+
44
+ ### 2. Access authentication state using `useMentraAuth`
45
+
46
+ In any component that needs user information:
47
+
48
+ ```tsx
49
+ // src/MyComponent.tsx
50
+ import React from 'react';
51
+ import { useMentraAuth } from '@mentra/react';
52
+
53
+ const MyComponent = () => {
54
+ const { userId, frontendToken, isLoading, error, isAuthenticated, logout } = useMentraAuth();
55
+
56
+ if (isLoading) {
57
+ return <p>Loading authentication...</p>;
58
+ }
59
+
60
+ if (error) {
61
+ return (
62
+ <div>
63
+ <p>Authentication Error: {error}</p>
64
+ <p>Please ensure you are opening this page from the MentraOS app.</p>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ if (!isAuthenticated || !userId) {
70
+ return <p>Not authenticated. Please open from the MentraOS manager app.</p>;
71
+ }
72
+
73
+ // You are authenticated!
74
+ return (
75
+ <div>
76
+ <h1>Welcome, MentraOS User!</h1>
77
+ <p>User ID: {userId}</p>
78
+ {/* frontendToken is a JWT. Displaying it directly is usually for debugging. */}
79
+ <p>Your Frontend Token (for backend calls): <small>{frontendToken ? frontendToken.substring(0, 20) + '...' : 'N/A'}</small></p>
80
+ <button onClick={logout}>Logout</button>
81
+ {/* Add your webview content here */}
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export default MyComponent;
87
+ ```
88
+
89
+ ## How It Works
90
+
91
+ 1. When your webview is loaded by the MentraOS manager, it appends an `aos_signed_user_token` (a JWT) as a URL query parameter.
92
+ 2. The `MentraAuthProvider` attempts to find this token.
93
+ 3. It verifies the token's signature against the MentraOS Cloud public key and checks its claims (like issuer and expiration).
94
+ 4. If valid, it extracts the `userId` (from the `sub` claim) and a `frontendToken` (another JWT from the payload).
95
+ 5. These `userId` and `frontendToken` are then stored in `localStorage` and made available via the `useMentraAuth` hook.
96
+ 6. If the token is not found in the URL (e.g., on a page refresh within the webview), the provider attempts to load the `userId` and `frontendToken` from `localStorage`.
97
+
98
+ ## Making Authenticated Calls to Your App Backend
99
+
100
+ The `frontendToken` obtained from `useMentraAuth` is a JWT. You should send this token in the `Authorization` header as a Bearer token when making requests from your webview to **your app's backend API**. The MentraOS SDK will automatically verify this token.
101
+
102
+ ```typescript
103
+ // Example of an authenticated API call
104
+ const { frontendToken } = useMentraAuth();
105
+
106
+ async function fetchDataFromMyBackend(): Promise<void> {
107
+ if (!frontendToken) {
108
+ console.error("No frontend token available for backend call.");
109
+ return;
110
+ }
111
+
112
+ try {
113
+ const response = await fetch('https://your-app-backend.example.com/api/data', {
114
+ method: 'GET',
115
+ headers: {
116
+ 'Content-Type': 'application/json',
117
+ 'Authorization': `Bearer ${frontendToken}`,
118
+ },
119
+ });
120
+
121
+ if (!response.ok) {
122
+ throw new Error(`Backend request failed: ${response.status}`);
123
+ }
124
+
125
+ const data = await response.json();
126
+ console.log('Data from backend:', data);
127
+ // Process data
128
+ } catch (error) {
129
+ console.error('Error fetching data from backend:', error);
130
+ }
131
+ }
132
+ ```
133
+
134
+ > **Note:**
135
+ > If your app webview is hosted on a different domain or port than your backend API, make sure your backend's CORS (Cross-Origin Resource Sharing) policy allows requests from the webview's origin.
136
+ > For example, if your backend is at `https://your-app-backend.example.com` and your webview is loaded from `https://some-other-frontend.com`, your backend must explicitly allow cross-origin requests from `https://some-other-frontend.com` (or use a wildcard for development, but restrict in production).
137
+ >
138
+ > **Example in the backend:**
139
+ > ```typescript
140
+ > import cors from 'cors';
141
+ >
142
+ > // Allow only your webview's origin in production
143
+ > app.use(cors({
144
+ > origin: 'https://some-other-frontend.com', // Replace with your actual webview origin
145
+ > credentials: true, // If you use cookies
146
+ > }));
147
+ > ```
148
+ >
149
+ > Failing to set the correct CORS policy will result in browser errors when your webview tries to call your backend API.
150
+
151
+
152
+ ## TypeScript Support
153
+
154
+ This library includes full TypeScript support. The `useMentraAuth` hook returns a typed object with the following interface:
155
+
156
+ ```typescript
157
+ interface MentraAuthContextType {
158
+ userId: string | null;
159
+ frontendToken: string | null;
160
+ isLoading: boolean;
161
+ error: string | null;
162
+ isAuthenticated: boolean;
163
+ logout: () => void;
164
+ }
165
+ ```
166
+
167
+ ## Troubleshooting
168
+
169
+ * **"Token validation failed" / "Token expired":**
170
+ * Ensure the clock on the device running the MentraOS manager is synchronized.
171
+ * The `gracePeriod` in `authCore.ts` (currently 120 seconds) can be adjusted if clock skew is a persistent issue, but large grace periods reduce security.
172
+ * The MentraOS manager app should be providing a fresh token.
173
+ * **"MentraOS signed user token not found":**
174
+ * Make sure your webview is being launched from the MentraOS manager app.
175
+ * Verify the manager app is correctly appending `?aos_signed_user_token=...` to your webview URL.
176
+ * **`jsrsasign` errors:** Ensure `jsrsasign` is correctly installed and accessible in your frontend build.
177
+ * **Backend Authentication Issues:**
178
+ * Ensure you're using `Authorization: Bearer ${frontendToken}` header format, not query parameters.
179
+ * Verify your backend allows CORS requests from the domain of your frontend.
@@ -0,0 +1,13 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { AuthState } from './lib/authCore';
3
+ export interface MentraAuthContextType extends AuthState {
4
+ isLoading: boolean;
5
+ error: string | null;
6
+ logout: () => void;
7
+ isAuthenticated: boolean;
8
+ }
9
+ export declare const MentraAuthContext: React.Context<MentraAuthContextType | undefined>;
10
+ export declare const MentraAuthProvider: ({ children }: {
11
+ children: ReactNode;
12
+ }) => import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=AuthProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAsC,SAAS,EAAe,MAAM,OAAO,CAAC;AAC1F,OAAO,EAAmC,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE5E,MAAM,WAAW,qBAAsB,SAAQ,SAAS;IACtD,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,iBAAiB,kDAA8D,CAAC;AAE7F,eAAO,MAAM,kBAAkB,GAAI,cAAc;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,4CAyCvE,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // augmentos-react/src/AuthProvider.tsx
3
+ import { createContext, useState, useEffect, useCallback } from 'react';
4
+ import { initializeAuth, clearStoredAuth } from './lib/authCore';
5
+ export const MentraAuthContext = createContext(undefined);
6
+ export const MentraAuthProvider = ({ children }) => {
7
+ const [userId, setUserId] = useState(null);
8
+ const [frontendToken, setFrontendToken] = useState(null);
9
+ const [isLoading, setIsLoading] = useState(true);
10
+ const [error, setError] = useState(null);
11
+ const loadAuth = useCallback(async () => {
12
+ setIsLoading(true);
13
+ setError(null);
14
+ try {
15
+ const auth = await initializeAuth();
16
+ setUserId(auth.userId);
17
+ setFrontendToken(auth.frontendToken);
18
+ }
19
+ catch (e) {
20
+ console.error("Augmentos Auth Initialization Error:", e);
21
+ setError(e.message || 'Unknown authentication error');
22
+ clearStoredAuth(); // Clear any potentially bad stored state
23
+ setUserId(null);
24
+ setFrontendToken(null);
25
+ }
26
+ finally {
27
+ setIsLoading(false);
28
+ }
29
+ }, []);
30
+ useEffect(() => {
31
+ loadAuth();
32
+ }, [loadAuth]);
33
+ const logout = useCallback(() => {
34
+ clearStoredAuth();
35
+ setUserId(null);
36
+ setFrontendToken(null);
37
+ }, []);
38
+ const isAuthenticated = !!userId && !!frontendToken;
39
+ return (_jsx(MentraAuthContext.Provider, { value: { userId, frontendToken, isLoading, error, logout, isAuthenticated }, children: children }));
40
+ };
41
+ //# sourceMappingURL=AuthProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,OAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAa,WAAW,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,eAAe,EAAa,MAAM,gBAAgB,CAAC;AAS5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAoC,SAAS,CAAC,CAAC;AAE7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAAE,QAAQ,EAA2B,EAAE,EAAE;IAC1E,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACtC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;YACpC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,gBAAgB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,CAAC,CAAC,CAAC;YACzD,QAAQ,CAAE,CAAW,CAAC,OAAO,IAAI,8BAA8B,CAAC,CAAC;YACjE,eAAe,EAAE,CAAC,CAAC,yCAAyC;YAC5D,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,EAAE,CAAC;IACb,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,eAAe,EAAE,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,aAAa,CAAC;IAEpD,OAAO,CACL,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,YACpG,QAAQ,GACkB,CAC9B,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { MentraAuthProvider } from './AuthProvider';
2
+ export { useMentraAuth } from './useMentraAuth';
3
+ export type { AuthState } from './lib/authCore';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // augmentos-react/src/index.ts
2
+ export { MentraAuthProvider } from './AuthProvider';
3
+ export { useMentraAuth } from './useMentraAuth';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface AuthState {
2
+ userId: string | null;
3
+ frontendToken: string | null;
4
+ }
5
+ /**
6
+ * Initializes authentication by checking for tokens in URL parameters or localStorage
7
+ * @returns Promise that resolves to the current authentication state
8
+ */
9
+ export declare function initializeAuth(): Promise<AuthState>;
10
+ export declare function getStoredAuth(): AuthState;
11
+ export declare function clearStoredAuth(): void;
12
+ //# sourceMappingURL=authCore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authCore.d.ts","sourceRoot":"","sources":["../../src/lib/authCore.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA0DD;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,SAAS,CAAC,CAkCzD;AAED,wBAAgB,aAAa,IAAI,SAAS,CAIzC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAGtC"}
@@ -0,0 +1,111 @@
1
+ // augmentos-react/src/lib/authCore.ts
2
+ import { KEYUTIL, KJUR } from 'jsrsasign'; // Assuming jsrsasign is available
3
+ // This should be the AugmentOS Cloud's public key for verifying aos_signed_user_token
4
+ // It's a public key, so embedding it is generally fine.
5
+ const userTokenPublicKeyPEM = `-----BEGIN PUBLIC KEY-----
6
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Yt2RtNOdeKQxWMY0c84
7
+ ADpY1Jy58YWZhaEgP2A5tBwFUKgy/TH9gQLWZjQ3dQ/6XXO8qq0kluoYFqM7ZDRF
8
+ zJ0E4Yi0WQncioLRcCx4q8pDmqY9vPKgv6PruJdFWca0l0s3gZ3BqSeWum/C23xK
9
+ FPHPwi8gvRdc6ALrkcHeciM+7NykU8c0EY8PSitNL+Tchti95kGu+j6APr5vNewi
10
+ zRpQGOdqaLWe+ahHmtj6KtUZjm8o6lan4f/o08C6litizguZXuw2Nn/Kd9fFI1xF
11
+ IVNJYMy9jgGaOi71+LpGw+vIpwAawp/7IvULDppvY3DdX5nt05P1+jvVJXPxMKzD
12
+ TQIDAQAB
13
+ -----END PUBLIC KEY-----`;
14
+ const USER_ID_KEY = 'augmentos_userId';
15
+ const FRONTEND_TOKEN_KEY = 'augmentos_frontendToken';
16
+ /**
17
+ * Verifies and parses a signed user token using the AugmentOS Cloud public key
18
+ * @param signedUserToken - The JWT token to verify and parse
19
+ * @returns Promise that resolves to the parsed payload or null if invalid
20
+ */
21
+ async function verifyAndParseToken(signedUserToken) {
22
+ try {
23
+ const publicKeyObj = KEYUTIL.getKey(userTokenPublicKeyPEM);
24
+ // verifyJWT will check signature, nbf, exp.
25
+ // It will also check 'iss' if provided in the options.
26
+ const isValid = KJUR.jws.JWS.verifyJWT(signedUserToken, publicKeyObj, {
27
+ alg: ['RS256'], // Specify expected algorithms
28
+ iss: ['https://prod.augmentos.cloud'], // Specify expected issuer
29
+ // jsrsasign's verifyJWT checks 'nbf' and 'exp' by default.
30
+ // Grace period for clock skew
31
+ gracePeriod: 120, // 2 minutes in seconds
32
+ });
33
+ if (!isValid) {
34
+ // Parse the token to get header and payload for debugging
35
+ const parsedJWT = KJUR.jws.JWS.parse(signedUserToken);
36
+ if (parsedJWT) {
37
+ console.warn('Token validation failed. Header:', parsedJWT.headerObj, 'Payload:', parsedJWT.payloadObj);
38
+ // Check expiration manually for more detailed logging if needed
39
+ const payload = parsedJWT.payloadObj;
40
+ if (payload && payload.exp) {
41
+ const now = KJUR.jws.IntDate.get('now');
42
+ if (payload.exp < now - 120) { // Check with grace period
43
+ console.warn(`Token expired at ${new Date(payload.exp * 1000).toISOString()}`);
44
+ }
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ const parsedJWT = KJUR.jws.JWS.parse(signedUserToken);
50
+ if (!parsedJWT || !parsedJWT.payloadObj) {
51
+ console.error('Failed to parse JWT payload.');
52
+ return null;
53
+ }
54
+ const payload = parsedJWT.payloadObj;
55
+ if (!payload.sub || !payload.frontendToken) {
56
+ console.error('Parsed payload missing sub (userId) or frontendToken.');
57
+ return null;
58
+ }
59
+ return payload;
60
+ }
61
+ catch (e) {
62
+ console.error('[verifyAndParseToken] Error verifying token:', e);
63
+ return null;
64
+ }
65
+ }
66
+ /**
67
+ * Initializes authentication by checking for tokens in URL parameters or localStorage
68
+ * @returns Promise that resolves to the current authentication state
69
+ */
70
+ export async function initializeAuth() {
71
+ const params = new URLSearchParams(window.location.search);
72
+ const tokenFromUrl = params.get('aos_signed_user_token');
73
+ if (tokenFromUrl) {
74
+ const payload = await verifyAndParseToken(tokenFromUrl);
75
+ if (payload) {
76
+ localStorage.setItem(USER_ID_KEY, payload.sub);
77
+ localStorage.setItem(FRONTEND_TOKEN_KEY, payload.frontendToken);
78
+ // Remove the token from URL to prevent it from being bookmarked or shared.
79
+ params.delete('aos_signed_user_token');
80
+ window.history.replaceState({}, document.title, `${window.location.pathname}?${params.toString()}`);
81
+ return { userId: payload.sub, frontendToken: payload.frontendToken };
82
+ }
83
+ else {
84
+ // Token from URL was invalid, clear any stored ones
85
+ clearStoredAuth();
86
+ return { userId: null, frontendToken: null };
87
+ }
88
+ }
89
+ // If no token in URL, try to load from localStorage
90
+ const storedUserId = localStorage.getItem(USER_ID_KEY);
91
+ const storedFrontendToken = localStorage.getItem(FRONTEND_TOKEN_KEY);
92
+ if (storedUserId && storedFrontendToken) {
93
+ // For SPAs, if the token was already verified and its parts stored,
94
+ // we might trust these for the current session.
95
+ // A full re-verification of a stored *signedUserToken* would be more secure
96
+ // but adds complexity if the signed token isn't always re-provided.
97
+ // The current approach: verify once from URL, then use stored parts.
98
+ return { userId: storedUserId, frontendToken: storedFrontendToken };
99
+ }
100
+ return { userId: null, frontendToken: null };
101
+ }
102
+ export function getStoredAuth() {
103
+ const userId = localStorage.getItem(USER_ID_KEY);
104
+ const frontendToken = localStorage.getItem(FRONTEND_TOKEN_KEY);
105
+ return { userId, frontendToken };
106
+ }
107
+ export function clearStoredAuth() {
108
+ localStorage.removeItem(USER_ID_KEY);
109
+ localStorage.removeItem(FRONTEND_TOKEN_KEY);
110
+ }
111
+ //# sourceMappingURL=authCore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authCore.js","sourceRoot":"","sources":["../../src/lib/authCore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { MentraAuthContextType } from './AuthProvider';
2
+ /**
3
+ * Custom hook to access the Augmentos authentication context.
4
+ *
5
+ * @returns {MentraAuthContextType} The authentication context containing user state,
6
+ * loading status, error information, and authentication methods.
7
+ *
8
+ * @throws {Error} When used outside of an MentraAuthProvider component.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { userId, isAuthenticated, logout, isLoading } = useMentraAuth();
13
+ * ```
14
+ */
15
+ export declare const useMentraAuth: () => MentraAuthContextType;
16
+ //# sourceMappingURL=useMentraAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMentraAuth.d.ts","sourceRoot":"","sources":["../src/useMentraAuth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,QAAO,qBAMhC,CAAC"}
@@ -0,0 +1,24 @@
1
+ // augmentos-react/src/useMentraAuth.ts
2
+ import { useContext } from 'react';
3
+ import { MentraAuthContext } from './AuthProvider';
4
+ /**
5
+ * Custom hook to access the Augmentos authentication context.
6
+ *
7
+ * @returns {MentraAuthContextType} The authentication context containing user state,
8
+ * loading status, error information, and authentication methods.
9
+ *
10
+ * @throws {Error} When used outside of an MentraAuthProvider component.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { userId, isAuthenticated, logout, isLoading } = useMentraAuth();
15
+ * ```
16
+ */
17
+ export const useMentraAuth = () => {
18
+ const context = useContext(MentraAuthContext);
19
+ if (context === undefined) {
20
+ throw new Error('useMentraAuth must be used within an MentraAuthProvider');
21
+ }
22
+ return context;
23
+ };
24
+ //# sourceMappingURL=useMentraAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMentraAuth.js","sourceRoot":"","sources":["../src/useMentraAuth.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAyB,MAAM,gBAAgB,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAA0B,EAAE;IACvD,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@mentra/react",
3
+ "version": "0.2.0",
4
+ "description": "React hooks and providers for MentraOS webviews, allowing you to create frontends for smartglasses apps.",
5
+ "author": "Mentra Labs",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.cjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "rm -rf dist && bun x tsc -p tsconfig.json",
24
+ "dev": "bun x tsc -p tsconfig.json --watch",
25
+ "lint": "eslint src --ext .ts,.tsx",
26
+ "test": "echo \"Error: no test specified\" && exit 1"
27
+ },
28
+ "devDependencies": {
29
+ "@types/jsrsasign": "^10.5.15",
30
+ "@types/react": "^19.1.6",
31
+ "jsrsasign": "^11.1.0",
32
+ "react": "^19.1.0",
33
+ "typescript": "^5.0.0"
34
+ },
35
+ "keywords": [
36
+ "augmentos",
37
+ "react",
38
+ "mentra",
39
+ "mentraos"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }