fix: go mod oauth test
All checks were successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/playwright Pipeline was successful
ci/woodpecker/push/deplyoment Pipeline was successful

This commit is contained in:
kekskurse 2025-05-25 20:49:24 +02:00
parent 548266fe9d
commit 92704e80f1
8 changed files with 175 additions and 2 deletions

View file

@ -14,4 +14,9 @@
| SMTP_PASSWORD | SMTP Password | `none` |
| SMTP_FROM | Mail address used as from for all mails | `none` |
| WEB_PUBLIC_REGISTRATION | Enabled the registration for anyone on the webgui | False |
| WEB_JWT_SECRET_KEY | Set secret key for the jwt used | `abc` |
| MINIAUTH_LOGIN_WITH_NOT_APPROVED_MAIL | Allow user to login even if there mail address is not validated | False |
## Development
create locale db by run `sh db.sh` than set the env varieables you need and run everything.

View file

@ -7,6 +7,7 @@ import (
"html/template"
"git.keks.cloud/kekskurse/miniauth/pkg/miniauth"
"git.keks.cloud/kekskurse/miniauth/pkg/oauthapi"
"git.keks.cloud/kekskurse/miniauth/pkg/smtpclient"
"git.keks.cloud/kekskurse/miniauth/pkg/userstore"
"git.keks.cloud/kekskurse/miniauth/pkg/web"
@ -31,6 +32,7 @@ type gloableConfig struct {
WebConfig web.WebConfig `env:", prefix=WEB_"`
MiniauthConfig miniauth.MiniauthConfig `env:", prefix=MINIAUTH_"`
DummyDatabase bool `env:"DUMMY_DATABASE"`
OauthConfig oauthapi.OauthAPIConf `env:", prefix=OAUTH_"`
}
func config() gloableConfig {
@ -79,6 +81,9 @@ func setupRouter(cfg gloableConfig) *gin.Engine {
webServer := web.NewWeb(cfg.WebConfig, ma)
webServer.RegisterRoutes(routesWeb)
oauthapi := oauthapi.NewOauthAPI(cfg.OauthConfig, ma)
oauthapi.RegisterRoutes(router.Group("/oauth/"))
router.GET("/", func(c *gin.Context) {})
router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") })

View file

@ -0,0 +1 @@
-- Write your down sql migration here

View file

@ -0,0 +1,12 @@
-- Write your up sql migration here
CREATE TABLE clients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
client_id TEXT NOT NULL UNIQUE,
client_secret TEXT NOT NULL UNIQUE,
redirect_uris TEXT NOT NULL,
response_types TEXT NOT NULL,
grant_types TEXT NOT NULL,
scopes TEXT NOT NULL
)

109
pkg/oauthapi/oauthapi.go Normal file
View file

@ -0,0 +1,109 @@
package oauthapi
import (
"crypto/rand"
"crypto/rsa"
"net/http"
"time"
"git.keks.cloud/kekskurse/miniauth/pkg/miniauth"
"github.com/gin-gonic/gin"
"github.com/ory/fosite"
"github.com/ory/fosite/compose"
"github.com/ory/fosite/storage"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
type OauthAPIConf struct{}
type OAuthAPI struct {
config OauthAPIConf
ma miniauth.Miniauth
log zerolog.Logger
oauth fosite.OAuth2Provider
}
func NewOauthAPI(config OauthAPIConf, ma miniauth.Miniauth) OAuthAPI {
w := OAuthAPI{}
w.config = config
w.ma = ma
l := log.With().Str("pkg", "oauthapi").Logger()
w.log = l
storage := storage.NewExampleStore()
secret := []byte("my super secret signing password")
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
w.log.Fatal().Err(err).Msg("cant create privatekey")
}
oconfig := &fosite.Config{
AccessTokenLifespan: time.Minute * 30,
GlobalSecret: secret,
}
oauth2Provider := compose.ComposeAllEnabled(oconfig, storage, privateKey)
w.oauth = oauth2Provider
return w
}
func (w OAuthAPI) RegisterRoutes(routing *gin.RouterGroup) error {
routing.GET("/auth", w.authGet)
routing.POST("/auth", w.authPost)
routing.POST("/token", w.token)
return nil
}
func (w OAuthAPI) authGet(ctx *gin.Context) {
ar, err := w.oauth.NewAuthorizeRequest(ctx, ctx.Request)
if err != nil {
log.Error().Err(err).Msg("cant create authorize request")
w.oauth.WriteAuthorizeError(ctx, ctx.Writer, ar, err)
return
}
w.log.Debug().Interface("ar", err).Msg("AuthorizeRequest")
ctx.HTML(http.StatusOK, "login.html", nil)
}
func (w OAuthAPI) authPost(ctx *gin.Context) {
ar, err := w.oauth.NewAuthorizeRequest(ctx, ctx.Request)
if err != nil {
log.Error().Err(err).Msg("cant create authorize request")
w.oauth.WriteAuthorizeError(ctx, ctx.Writer, ar, err)
return
}
mySessionData := &fosite.DefaultSession{
Username: ctx.PostForm("username"),
}
response, err := w.oauth.NewAuthorizeResponse(ctx.Request.Context(), ar, mySessionData)
if err != nil {
log.Error().Err(err).Msg("cant create response")
w.oauth.WriteAuthorizeError(ctx, ctx.Writer, ar, err)
return
}
w.oauth.WriteAuthorizeResponse(ctx, ctx.Writer, ar, response)
}
func (w OAuthAPI) token(ctx *gin.Context) {
mySessionData := new(fosite.DefaultSession)
accessRequest, err := w.oauth.NewAccessRequest(ctx, ctx.Request, mySessionData)
if err != nil {
w.oauth.WriteAccessError(ctx.Request.Context(), ctx.Writer, accessRequest, err)
return
}
response, err := w.oauth.NewAccessResponse(ctx, accessRequest)
if err != nil {
w.oauth.WriteAccessError(ctx, ctx.Writer, accessRequest, err)
return
}
w.oauth.WriteAccessResponse(ctx, ctx.Writer, accessRequest, response)
}

10
pkg/oauthapi/store.go Normal file
View file

@ -0,0 +1,10 @@
package oauthapi
import "github.com/rs/zerolog"
type Config struct {
SQLite struct {
Path string `env:"SQLITE_PATH"`
}
Logger zerolog.Logger
}

View file

@ -3,13 +3,17 @@ package web
import (
"errors"
"net/http"
"time"
"git.keks.cloud/kekskurse/miniauth/pkg/miniauth"
"git.keks.cloud/kekskurse/miniauth/pkg/userstore"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
type WebConfig struct {
PublicRegistration bool `env:"PUBLIC_REGISTRATION, default=0"`
JWT_SECRET_KEY string `env:"JWT_SECRET_KEY, default=abc"`
}
type Web struct {
@ -68,11 +72,37 @@ func (w Web) PostLoginPage(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
err := w.ma.UserLogin(username, password)
_, err := w.ma.UserLogin(username, password)
if err != nil {
c.HTML(http.StatusOK, "login.html", gin.H{"msg": errors.Unwrap(err).Error()})
return
}
c.SetSameSite(http.SameSiteStrictMode)
c.HTML(http.StatusOK, "msg.html", gin.H{"msg": "Login ok!"})
}
func (w Web) DashboardPage(c *gin.Context) {
}
type UserClaim struct {
User userstore.User
jwt.RegisteredClaims
}
func (w Web) createToken(user userstore.User) (string, error) {
tokenLifespan := 15 * time.Minute
claims := UserClaim{
User: user,
RegisteredClaims: jwt.RegisteredClaims{
Subject: "user-" + user.Username,
Issuer: "gin-jwt",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(tokenLifespan)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return t.SignedString(w.config.JWT_SECRET_KEY)
}

View file

@ -31,6 +31,7 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
video: 'on',
},
/* Configure projects for major browsers */