diff --git a/backend/graph/schema.resolvers.go b/backend/graph/schema.resolvers.go index beeaa9c..ded2651 100644 --- a/backend/graph/schema.resolvers.go +++ b/backend/graph/schema.resolvers.go @@ -44,7 +44,7 @@ func (r *mutationResolver) Login(ctx context.Context, input model.LoginInput) (* "access_token", tokens.AccessToken, int(r.Store.Auth.Config.AccessTokenLifetime.Seconds()), - r.Store.Auth.Config.Endpoint, + "/", r.Store.Auth.Config.Domain, true, true, ) @@ -52,7 +52,7 @@ func (r *mutationResolver) Login(ctx context.Context, input model.LoginInput) (* "refresh_token", tokens.RefreshToken, int(r.Store.Auth.Config.RefreshTokenLifetime.Seconds()), - r.Store.Auth.Config.Endpoint, + "/", r.Store.Auth.Config.Domain, true, true, ) @@ -112,7 +112,7 @@ func (r *mutationResolver) RefreshToken(ctx context.Context) (*model.AuthPayload "access_token", tokens.AccessToken, int(r.Store.Auth.Config.AccessTokenLifetime.Seconds()), - r.Store.Auth.Config.Endpoint, + "/", r.Store.Auth.Config.Domain, true, true, ) @@ -120,7 +120,7 @@ func (r *mutationResolver) RefreshToken(ctx context.Context) (*model.AuthPayload "refresh_token", tokens.RefreshToken, int(r.Store.Auth.Config.RefreshTokenLifetime.Seconds()), - r.Store.Auth.Config.Endpoint, + "/", r.Store.Auth.Config.Domain, true, true, ) diff --git a/backend/handlers/handle_auth.go b/backend/handlers/handle_auth.go index 16b4c22..c009a85 100644 --- a/backend/handlers/handle_auth.go +++ b/backend/handlers/handle_auth.go @@ -50,6 +50,28 @@ func (store *Store) AdminMiddleware(ctx *gin.Context) { ctx.Next() } +func (store *Store) ValidateAdmin(ctx *gin.Context) { + accessToken, err := ctx.Cookie("access_token") + if err != nil { + ctx.Status(http.StatusUnauthorized) + return + } + + claims, err := store.Auth.VerifyJWT(accessToken) + if err != nil { + ctx.Status(http.StatusUnauthorized) + return + } + + admin, ok := (*claims)["admin"].(bool) + if !ok || !admin { + ctx.Status(http.StatusForbidden) + return + } + + ctx.Status(http.StatusOK) +} + func (store *Store) CheckToken(ctx *gin.Context) { access_token, err := ctx.Cookie("access_token") if err != nil { @@ -123,7 +145,7 @@ func (store *Store) RefreshToken(ctx *gin.Context) { "access_token", tokens.AccessToken, int(store.Auth.Config.AccessTokenLifetime.Seconds()), - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -131,7 +153,7 @@ func (store *Store) RefreshToken(ctx *gin.Context) { "refresh_token", tokens.RefreshToken, int(store.Auth.Config.RefreshTokenLifetime.Seconds()), - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -169,7 +191,7 @@ func (store *Store) Login(ctx *gin.Context) { "access_token", tokens.AccessToken, int(store.Auth.Config.AccessTokenLifetime.Seconds()), - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -177,7 +199,7 @@ func (store *Store) Login(ctx *gin.Context) { "refresh_token", tokens.RefreshToken, int(store.Auth.Config.RefreshTokenLifetime.Seconds()), - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -197,7 +219,7 @@ func (store *Store) removeCookies(ctx *gin.Context) { "access_token", "", -1, - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -205,7 +227,7 @@ func (store *Store) removeCookies(ctx *gin.Context) { "refresh_token", "", -1, - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) diff --git a/backend/handlers/handle_user.go b/backend/handlers/handle_user.go index 99a1eba..753cb45 100644 --- a/backend/handlers/handle_user.go +++ b/backend/handlers/handle_user.go @@ -173,7 +173,7 @@ func (store *Store) DeleteUser(ctx *gin.Context) { "access_token", "", -1, - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) @@ -181,7 +181,7 @@ func (store *Store) DeleteUser(ctx *gin.Context) { "refresh_token", "", -1, - store.Auth.Config.Endpoint, + "/", store.Auth.Config.Domain, true, true, ) diff --git a/backend/main.go b/backend/main.go index 29fb0dc..b741a31 100644 --- a/backend/main.go +++ b/backend/main.go @@ -70,7 +70,7 @@ func main() { authSecret := os.Getenv("BACKEND_SECRET") backendEndpoint := os.Getenv("BACKEND_ENDPOINT") - accessTokenLifetime := 24 * time.Hour + accessTokenLifetime := 7 * 24 * time.Hour refreshTokenLifetime := 365 * 24 * time.Hour authConfig := services.AuthConfig{Secret: []byte(authSecret), Domain: domainName, RefreshTokenLifetime: refreshTokenLifetime, AccessTokenLifetime: accessTokenLifetime, Endpoint: backendEndpoint} auth := services.InitAuth(&authConfig) @@ -122,6 +122,7 @@ func main() { r.POST("/auth/refresh", store.RefreshToken) r.GET("/auth/check", store.CheckToken) r.POST("/auth/logout", store.Logout) + r.GET("/auth/validate-admin", store.ValidateAdmin) // SPOTIFY r.GET("/spotify/callback", store.CompleteSpotifyAuth) diff --git a/docker-compose.yml b/docker-compose.yml index 1b3fd06..b2c895d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,6 @@ services: - backend - icecast2 - gitea - - hasura - quartz - searxng networks: @@ -96,6 +95,8 @@ services: image: hasura/graphql-engine:v2.44.0 container_name: "${HASURA_HOST}" restart: always + profiles: + - disabled depends_on: - db networks: @@ -135,7 +136,6 @@ services: volumes: - ${OBSIDIAN_DIR}:/quartz/content:ro - searxng: build: context: ./searxng @@ -151,7 +151,6 @@ services: volumes: - searxng_data:/etc/searxng - gitea: image: docker.gitea.com/gitea:1.25.4-rootless container_name: "${GITEA_HOST}" diff --git a/nginx/nginx.conf.template b/nginx/nginx.conf.template index 63bc3d1..e0a62f2 100644 --- a/nginx/nginx.conf.template +++ b/nginx/nginx.conf.template @@ -207,6 +207,8 @@ http { } location /hasura/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$HASURA_HOST:$HASURA_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -222,6 +224,8 @@ http { } location /notes/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -233,11 +237,25 @@ http { } + location = /internal/auth/admin-validate { + internal; + proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/auth/validate-admin; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header Cookie $http_cookie; + } + + location @auth_denied { + return 302 /; + } + location /searxng { return 301 /searxng/; } location /searxng/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$SEARXNG_HOST:$SEARXNG_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/nginx/nginx_dev.conf.template b/nginx/nginx_dev.conf.template index 26d62d7..5762ccf 100644 --- a/nginx/nginx_dev.conf.template +++ b/nginx/nginx_dev.conf.template @@ -134,6 +134,8 @@ http { } location /hasura/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$HASURA_HOST:$HASURA_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -149,6 +151,8 @@ http { } location /notes/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -160,11 +164,25 @@ http { } + location = /internal/auth/admin-validate { + internal; + proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/auth/validate-admin; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header Cookie $http_cookie; + } + + location @auth_denied { + return 302 /; + } + location /searxng { return 301 /searxng/; } location /searxng/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$SEARXNG_HOST:$SEARXNG_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -272,6 +290,8 @@ http { } location /hasura/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$HASURA_HOST:$HASURA_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -287,6 +307,8 @@ http { } location /notes/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -298,11 +320,25 @@ http { } + location = /internal/auth/admin-validate { + internal; + proxy_pass http://$BACKEND_HOST:$BACKEND_PORT/auth/validate-admin; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header Cookie $http_cookie; + } + + location @auth_denied { + return 302 /; + } + location /searxng { return 301 /searxng/; } location /searxng/ { + auth_request /internal/auth/admin-validate; + error_page 401 403 = @auth_denied; proxy_pass http://$SEARXNG_HOST:$SEARXNG_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;