about summary refs log tree commit diff
path: root/nix
diff options
context:
space:
mode:
authorEmile <git@emile.space>2025-02-23 02:16:02 +0100
committerEmile <git@emile.space>2025-02-23 02:16:02 +0100
commit3bab9bb8db06c8c599454d2f353bd29bb480591e (patch)
tree1aac3c2aabf35030fb467505c0366431210681fc /nix
parent03527c5f1b857f10a757de50e344f887301add75 (diff)
corrino: goapp finally working with oidc HEAD main
Diffstat (limited to 'nix')
-rw-r--r--nix/hosts/corrino/secrets/goapp_oidc_secret.agebin395 -> 395 bytes
-rw-r--r--nix/hosts/corrino/secrets/goapp_oidc_session_key.age8
-rw-r--r--nix/hosts/corrino/www/goapp.emile.space.nix53
-rw-r--r--nix/modules/goapp-frontend/default.nix2
-rw-r--r--nix/templates/goapp/frontend/src/handlers.go65
-rw-r--r--nix/templates/goapp/frontend/src/init.go9
-rw-r--r--nix/templates/goapp/frontend/templates/index.html2
7 files changed, 92 insertions, 47 deletions
diff --git a/nix/hosts/corrino/secrets/goapp_oidc_secret.age b/nix/hosts/corrino/secrets/goapp_oidc_secret.age
index a842003..ca96981 100644
--- a/nix/hosts/corrino/secrets/goapp_oidc_secret.age
+++ b/nix/hosts/corrino/secrets/goapp_oidc_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/goapp_oidc_session_key.age b/nix/hosts/corrino/secrets/goapp_oidc_session_key.age
new file mode 100644
index 0000000..938f97f
--- /dev/null
+++ b/nix/hosts/corrino/secrets/goapp_oidc_session_key.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q BAvDdIEUEgmo963+9Wd5VLJgrp3MBISvWR3+27bfJW8
+NOEj2ObYe/cM2CaqzmbgexSEUNZIEo1ZmvlamJaSOjo
+-> ssh-ed25519 m8VklA d/hwd3rGkPD3GDdlOP2XUsi687VH+tfKrAsKnImk+kI
+gFAyyMZT5DK7da7YXOf/5gUd4Bi9cEe3ddMKUMuctMU
+--- 9tVE+AAvptrlMZe5+UGJGzH9usnxa+ZICbikcRT0PYI
+g!X+/šØ0P	;™*{ª«V p!¶êÙ4£sÌöãa	ÎI+À/\Iœ‚Ô–,Äéo½p‡Î웘DP7"ŠíØÀ¦ä;p·„PÀpÍ
+%ÈcÒUÚœ8
\ No newline at end of file
diff --git a/nix/hosts/corrino/www/goapp.emile.space.nix b/nix/hosts/corrino/www/goapp.emile.space.nix
index 4a486aa..361e95a 100644
--- a/nix/hosts/corrino/www/goapp.emile.space.nix
+++ b/nix/hosts/corrino/www/goapp.emile.space.nix
@@ -17,11 +17,10 @@
       id = "goapp";
 
       # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$/Ht5DUFmIeu/7Ty2PWHXnw$.uJIN1vmZMyGjCAoA0PzUcVaTMIH36AK80KvOZAHVXgLr1Y9ZOrRjoiwK.srHAO29mrcw1BNpCjFTYdWOoympg";
+      secret = "$pbkdf2-sha512$310000$LPXJRoGR9RyTcaT6cADljg$FK8RV5CnKj5ano4fXmRzzvXcX/00F7k/G6nd67t.8iewpwyq8FntV4JgYZSV8AynYMxz1qnL4j3BzITLCM0KgQ";
       public = false;
       authorization_policy = "two_factor";
       redirect_uris = [
-        # "http://localhost:8080/oauth2/callback"
         "https://goapp.emile.space/oauth2/callback"
       ];
       scopes = [
@@ -51,28 +50,40 @@
   # - build in order to get the new hash (nix build .#goapp-frontend-pkg)
   # - update hash in the package (//nix/templates/goapp/frontent/default.nix)
   # - deploy
+  #
+  # https://goapp.emile.space/oauth2/callback?code=authelia_ac_iZKCXtRMnj2yjUAmiSkg_LBWjiME2-ghE6KMkxdb6Zw.nDLgCVpu9ctH1llEKUml5rr8szd3bkZYaGa_MAOtNLI&iss=https%3A%2F%2Fsso.emile.space&scope=openid+profile+email+groups&state=random-string-here
+  #
+  # Unable to exchange authorization code for tokens
+  #
+  # unable to exchange authorization code for tokens: oauth2: "invalid_client" "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)."
 
-  # services.emile.goapp-frontend = {
-  #   enable = true;
-  #   package = pkgs.goapp-frontend;
+  services.emile.goapp-frontend = {
+    enable = true;
+    package = pkgs.goapp-frontend;
 
-  #   host = "127.0.0.1";
-  #   port = config.emile.ports.goapp-frontend;
-  #   public-url = "https://goapp-frontend.emile.space/";
+    host = "127.0.0.1";
+    port = config.emile.ports.goapp;
+    public-url = "https://goapp.emile.space/";
 
-  #   oidc = {
-  #     id = "goapp-frontend";
-  #     issuer = "https://sso.emile.space";
-  #     cookie-name = "oidc-client";
-  #     scopes = [ "openid" "profile" "email" "groups" ];
-  #     secret-path = "/run/goapp-frontend_oidc_secret";
-  #   };
+    oidc = {
+      id = "goapp";
+      issuer = "https://sso.emile.space";
+      cookie-name = "oidc-client";
+      scopes = [
+        "openid"
+        "profile"
+        "email"
+        "groups"
+      ];
+      # secret-path = "/run/goapp-frontend_oidc_secret";
+      secret-path = config.age.secrets.goapp_oidc_secret.path;
+    };
 
-  #   # TODO(emile): change these when going live
-  #   session-key-path = config.age.secrets.goapp-frontend_oidc_secret.path;
+    # TODO(emile): change these when going live
+    session-key-path = config.age.secrets.goapp_oidc_secret.path;
 
-  #   logfile-path = "/var/log/goapp-frontend.log";
-  #   database-path = "/var/lib/goapp-frontend/main.db";
-  #   sessiondb-path = "/var/lib/goapp-frontend/session.db";
-  # };
+    logfile-path = "/var/log/goapp-frontend.log";
+    database-path = "/var/lib/goapp-frontend/main.db";
+    sessiondb-path = "/var/lib/goapp-frontend/session.db";
+  };
 }
diff --git a/nix/modules/goapp-frontend/default.nix b/nix/modules/goapp-frontend/default.nix
index 31573f7..c5f62aa 100644
--- a/nix/modules/goapp-frontend/default.nix
+++ b/nix/modules/goapp-frontend/default.nix
@@ -114,7 +114,7 @@ with lib;
       };
       path = [ pkgs.goapp-frontend ];
       serviceConfig.ExecStart = ''
-        ${pkgs.goapp-frontend}/bin/goapp-frontend \
+        ${pkgs.goapp-frontend}/bin/frontend \
           --host ${cfg.host} \
           --port ${toString cfg.port} \
           --public-url ${cfg.public-url} \
diff --git a/nix/templates/goapp/frontend/src/handlers.go b/nix/templates/goapp/frontend/src/handlers.go
index b0bbf91..2cbacde 100644
--- a/nix/templates/goapp/frontend/src/handlers.go
+++ b/nix/templates/goapp/frontend/src/handlers.go
@@ -12,7 +12,7 @@ import (
 )
 
 func indexHandler(w http.ResponseWriter, r *http.Request) {
-	session, err := globalState.sessions.Get(r, "session")
+	session, err := globalState.sessions.Get(r, options.CookieName)
 	if err != nil {
 		log.Println("error getting the session")
 	}
@@ -37,9 +37,14 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
 			},
 		},
 	}
-	tpl.NextLinks = []Link{
-		{"Login", "/login"},
-	}
+
+	//  session.Values["id_token"] = claimsIDToken
+	//  session.Values["userinfo"] = claimsUserInfo
+	//  session.Values["logged"] = true
+	//
+	log.Println("logged", session.Values["logged"])
+	log.Println("id-token", session.Values["id_token"])
+	log.Println("userinfo", session.Values["userinfo"])
 
 	if logged, ok := session.Values["logged"].(bool); ok && logged {
 		tpl.LoggedIn = true
@@ -68,6 +73,10 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
 		tpl.Claims.UserInfo.Name = filterText(tpl.Claims.UserInfo.Name, options.Filters)
 		tpl.RawToken = rawTokens[tpl.Claims.IDToken.JWTIdentifier]
 		tpl.AuthorizeCodeURL = acURLs[tpl.Claims.IDToken.JWTIdentifier].String()
+
+		tpl.NextLinks = []Link{{"Logout", "/logout"}}
+	} else {
+		tpl.NextLinks = []Link{{"Login", "/login"}}
 	}
 
 	w.Header().Add("Content-Type", "text/html")
@@ -129,11 +138,11 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) {
 	http.Redirect(w, r, "/", http.StatusFound)
 }
 
-func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
+func oauthCallbackHandler(w http.ResponseWriter, r *http.Request) {
 	log.Println("hit the oauth callback handler")
-	if req.FormValue("error") != "" {
-		log.Printf("got an error from the idp: %s", req.FormValue("error"))
-		http.Redirect(res, req, fmt.Sprintf("/error?%s", req.Form.Encode()), http.StatusFound)
+	if r.FormValue("error") != "" {
+		log.Printf("got an error from the idp: %s", r.FormValue("error"))
+		http.Redirect(w, r, fmt.Sprintf("/error?%s", r.Form.Encode()), http.StatusFound)
 		return
 	}
 
@@ -145,24 +154,32 @@ func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
 		ok         bool
 	)
 
+	log.Println(r.URL)
+
 	// The state should be checked here in production
-	if token, err = oauth2Config.Exchange(req.Context(), req.URL.Query().Get("code")); err != nil {
+	if token, err = oauth2Config.Exchange(
+		r.Context(),
+		r.URL.Query().Get("code"),
+		//  oauth2.SetAuthURLParam("client_id", oauth2Config.ClientID),
+		//  oauth2.SetAuthURLParam("client_secret", oauth2Config.ClientSecret),
+	); err != nil {
 		log.Println("Unable to exchange authorization code for tokens")
-		writeErr(res, err, "unable to exchange authorization code for tokens", http.StatusInternalServerError)
+		log.Println(err)
+		writeErr(w, err, "unable to exchange authorization code for tokens", http.StatusInternalServerError)
 		return
 	}
 
 	// Extract the ID Token from OAuth2 token.
 	if idTokenRaw, ok = token.Extra("id_token").(string); !ok {
 		log.Println("missing id token")
-		writeErr(res, nil, "missing id token", http.StatusInternalServerError)
+		writeErr(w, nil, "missing id token", http.StatusInternalServerError)
 		return
 	}
 
 	// Parse and verify ID Token payload.
-	if idToken, err = verifier.Verify(req.Context(), idTokenRaw); err != nil {
+	if idToken, err = verifier.Verify(r.Context(), idTokenRaw); err != nil {
 		log.Printf("unable to verify id token or token is invalid: %+v", idTokenRaw)
-		writeErr(res, err, "unable to verify id token or token is invalid", http.StatusInternalServerError)
+		writeErr(w, err, "unable to verify id token or token is invalid", http.StatusInternalServerError)
 		return
 	}
 
@@ -171,15 +188,15 @@ func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
 
 	if err = idToken.Claims(&claimsIDToken); err != nil {
 		log.Printf("unable to decode id token claims: %+v", &claimsIDToken)
-		writeErr(res, err, "unable to decode id token claims", http.StatusInternalServerError)
+		writeErr(w, err, "unable to decode id token claims", http.StatusInternalServerError)
 		return
 	}
 
 	var userinfo *oidc.UserInfo
 
-	if userinfo, err = provider.UserInfo(req.Context(), oauth2.StaticTokenSource(token)); err != nil {
+	if userinfo, err = provider.UserInfo(r.Context(), oauth2.StaticTokenSource(token)); err != nil {
 		log.Printf("unable to retreive userinfo claims")
-		writeErr(res, err, "unable to retrieve userinfo claims", http.StatusInternalServerError)
+		writeErr(w, err, "unable to retrieve userinfo claims", http.StatusInternalServerError)
 		return
 	}
 
@@ -187,15 +204,15 @@ func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
 
 	if err = userinfo.Claims(&claimsUserInfo); err != nil {
 		log.Printf("unable to decode userinfo claims")
-		writeErr(res, err, "unable to decode userinfo claims", http.StatusInternalServerError)
+		writeErr(w, err, "unable to decode userinfo claims", http.StatusInternalServerError)
 		return
 	}
 
 	var session *sessions.Session
 
-	if session, err = globalState.sessions.Get(req, options.CookieName); err != nil {
+	if session, err = globalState.sessions.Get(r, options.CookieName); err != nil {
 		log.Printf("unable to get session from cookie")
-		writeErr(res, err, "unable to get session from cookie", http.StatusInternalServerError)
+		writeErr(w, err, "unable to get session from cookie", http.StatusInternalServerError)
 		return
 	}
 
@@ -203,11 +220,11 @@ func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
 	session.Values["userinfo"] = claimsUserInfo
 	session.Values["logged"] = true
 	rawTokens[claimsIDToken.JWTIdentifier] = idTokenRaw
-	acURLs[claimsIDToken.JWTIdentifier] = req.URL
+	acURLs[claimsIDToken.JWTIdentifier] = r.URL
 
-	if err = session.Save(req, res); err != nil {
+	if err = session.Save(r, w); err != nil {
 		log.Printf("unable to save session")
-		writeErr(res, err, "unable to save session", http.StatusInternalServerError)
+		writeErr(w, err, "unable to save session", http.StatusInternalServerError)
 		return
 	}
 
@@ -215,11 +232,11 @@ func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) {
 
 	if redirectUrl, ok = session.Values["redirect-url"].(string); ok {
 		log.Printf("all fine!")
-		http.Redirect(res, req, redirectUrl, http.StatusFound)
+		http.Redirect(w, r, redirectUrl, http.StatusFound)
 		return
 	}
 
-	http.Redirect(res, req, "/", http.StatusFound)
+	http.Redirect(w, r, "/", http.StatusFound)
 }
 
 func writeErr(w http.ResponseWriter, err error, msg string, statusCode int) {
diff --git a/nix/templates/goapp/frontend/src/init.go b/nix/templates/goapp/frontend/src/init.go
index dc0e252..75fd87d 100644
--- a/nix/templates/goapp/frontend/src/init.go
+++ b/nix/templates/goapp/frontend/src/init.go
@@ -2,8 +2,10 @@ package main
 
 import (
 	"context"
+	//  "crypto/tls"
 	"fmt"
 	"log"
+	//  "net/http"
 	"net/url"
 	"os"
 	"strings"
@@ -70,13 +72,15 @@ func oauth2Init() (err error) {
 	if err != nil {
 		panic(err)
 	}
-	clientSecret := string(clientSecretBytes)
+	clientSecret := strings.TrimSpace(string(clientSecretBytes))
 
 	log.Printf("[ ] ClientID: %s", options.ClientID)
 	log.Printf("[ ] ClientSecret: %s", clientSecret)
 	log.Printf("[ ] redirectURL: %s", redirectURL.String())
 	log.Printf("[ ] providerEndpoint: %+v", provider.Endpoint())
 	log.Printf("[ ] Scopes: %s", options.Scopes)
+	log.Printf("[ ] Endpoint: %+v", provider.Endpoint())
+
 	oauth2Config = oauth2.Config{
 		ClientID:     options.ClientID,
 		ClientSecret: clientSecret,
@@ -84,5 +88,8 @@ func oauth2Init() (err error) {
 		Endpoint:     provider.Endpoint(),
 		Scopes:       strings.Split(options.Scopes, ","),
 	}
+
+	oauth2Config.Endpoint.AuthStyle = oauth2.AuthStyleInParams
+
 	return nil
 }
diff --git a/nix/templates/goapp/frontend/templates/index.html b/nix/templates/goapp/frontend/templates/index.html
index 1d21f3d..e4693af 100644
--- a/nix/templates/goapp/frontend/templates/index.html
+++ b/nix/templates/goapp/frontend/templates/index.html
@@ -5,6 +5,8 @@
 
 <h1>goapp</h1>
 
+{{ . }}
+
 {{- if .LoggedIn }}
 <p id="welcome">Logged in as {{ or .Claims.UserInfo.PreferredUsername .Claims.IDToken.Subject "unknown" }}!</p>
 <p><a href="/logout" id="log-out">Log out</a></p>