diff --git a/main.go b/main.go
index fe42959..4c33ad4 100644
--- a/main.go
+++ b/main.go
@@ -89,7 +89,7 @@ func main() {
AuthURL: cfg.OAuth.AuthURL,
TokenURL: cfg.OAuth.TokenURL,
},
- RedirectURL: cfg.OAuth.AuthURL,
+ RedirectURL: fmt.Sprintf("http://localhost:%d/oauth/callback", cfg.Server.Port),
Scopes: []string{"profile", "email"},
}
@@ -127,9 +127,7 @@ func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
-
- // Serve static files
- e.GET("/*", echo.WrapHandler(http.FileServer(http.FS(staticFiles))))
+ e.Use(middleware.CORS())
// OAuth2 routes
e.GET("/oauth/login", handleOAuthLogin)
@@ -146,6 +144,9 @@ func main() {
})
})
+ // Serve static files - must be after API routes
+ e.GET("/*", echo.WrapHandler(http.FileServer(http.FS(staticFiles))))
+
// Start server
addr := fmt.Sprintf(":%d", cfg.Server.Port)
logger.Info("Starting server", zap.Int("port", cfg.Server.Port))
@@ -190,10 +191,25 @@ func handleOAuthCallback(c echo.Context) error {
}
logger.Info("OAuth token exchanged successfully", zap.String("access_token", token.AccessToken))
- return c.JSON(http.StatusOK, map[string]interface{}{
- "access_token": token.AccessToken,
- "expires_in": token.Expiry,
- })
+
+ // Return HTML that stores the token and redirects to the main app
+ html := fmt.Sprintf(`
+
+
+
+ Authentication Successful
+
+
+
+ Authentication successful. Redirecting...
+
+
+ `, token.AccessToken)
+
+ return c.HTML(http.StatusOK, html)
}
// Middleware to enforce OAuth authentication
diff --git a/web/src/App.vue b/web/src/App.vue
index 9497da7..248a2cc 100644
--- a/web/src/App.vue
+++ b/web/src/App.vue
@@ -1,9 +1,21 @@
-
+
+
+
diff --git a/web/src/pages/auth.vue b/web/src/pages/auth.vue
new file mode 100644
index 0000000..e671e50
--- /dev/null
+++ b/web/src/pages/auth.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
Welcome
+
Please sign in to continue
+
+
+
+
+
+ Sign in with OAuth
+
+
+
+
+
+
+
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
index 5d4b8bc..6bdab2f 100644
--- a/web/src/router/index.ts
+++ b/web/src/router/index.ts
@@ -14,6 +14,22 @@ const router = createRouter({
routes: setupLayouts(routes),
})
+// Add authentication guard
+router.beforeEach((to, from, next) => {
+ // Check if the user is authenticated
+ const isAuthenticated = !!localStorage.getItem('auth_token');
+
+ // If route requires auth and user is not authenticated, redirect to auth page
+ if (to.path !== '/auth' && !isAuthenticated) {
+ next('/auth');
+ } else if (to.path === '/auth' && isAuthenticated) {
+ // If user is already authenticated and tries to access auth page, redirect to home
+ next('/');
+ } else {
+ next();
+ }
+});
+
// Workaround for https://github.com/vitejs/vite/issues/11804
router.onError((err, to) => {
if (err?.message?.includes?.('Failed to fetch dynamically imported module')) {
diff --git a/web/src/stores/auth.ts b/web/src/stores/auth.ts
new file mode 100644
index 0000000..f72ef7d
--- /dev/null
+++ b/web/src/stores/auth.ts
@@ -0,0 +1,53 @@
+// Auth store to manage authentication state
+import { defineStore } from 'pinia'
+
+export const useAuthStore = defineStore('auth', {
+ state: () => ({
+ token: localStorage.getItem('auth_token') || null,
+ user: null as any | null,
+ }),
+
+ getters: {
+ isAuthenticated: (state) => !!state.token,
+ },
+
+ actions: {
+ setToken(token: string) {
+ this.token = token;
+ localStorage.setItem('auth_token', token);
+ },
+
+ setUser(user: any) {
+ this.user = user;
+ },
+
+ logout() {
+ this.token = null;
+ this.user = null;
+ localStorage.removeItem('auth_token');
+ },
+
+ async fetchUserInfo() {
+ if (!this.token) return;
+
+ try {
+ const response = await fetch('/api/v1/health', {
+ headers: {
+ 'Authorization': `Bearer ${this.token}`
+ }
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ this.setUser(data.user);
+ } else {
+ // If token is invalid, logout
+ this.logout();
+ }
+ } catch (error) {
+ console.error('Failed to fetch user info:', error);
+ this.logout();
+ }
+ }
+ }
+})