mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-04-28 16:38:12 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a736c4ad90 | |||
| 74901dd88f |
@@ -57,6 +57,16 @@ export default defineConfig({
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/robots.txt/, ""),
|
rewrite: (path) => path.replace(/^\/robots.txt/, ""),
|
||||||
},
|
},
|
||||||
|
"/authorize": {
|
||||||
|
target: "http://tinyauth-backend:3000/authorize",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/authorize/, ""),
|
||||||
|
bypass: (req) => {
|
||||||
|
if (req.method === "GET") {
|
||||||
|
return "/index.html";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
allowedHosts: true,
|
allowedHosts: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (app *BootstrapApp) setupRouter() (*gin.Engine, error) {
|
|||||||
|
|
||||||
oauthController.SetupRoutes()
|
oauthController.SetupRoutes()
|
||||||
|
|
||||||
oidcController := controller.NewOIDCController(controller.OIDCControllerConfig{}, app.services.oidcService, apiRouter)
|
oidcController := controller.NewOIDCController(controller.OIDCControllerConfig{}, app.services.oidcService, apiRouter, engine)
|
||||||
|
|
||||||
oidcController.SetupRoutes()
|
oidcController.SetupRoutes()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,6 +22,7 @@ type OIDCController struct {
|
|||||||
config OIDCControllerConfig
|
config OIDCControllerConfig
|
||||||
router *gin.RouterGroup
|
router *gin.RouterGroup
|
||||||
oidc *service.OIDCService
|
oidc *service.OIDCService
|
||||||
|
engine *gin.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizeCallback struct {
|
type AuthorizeCallback struct {
|
||||||
@@ -57,11 +59,12 @@ type ClientCredentials struct {
|
|||||||
ClientSecret string
|
ClientSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOIDCController(config OIDCControllerConfig, oidcService *service.OIDCService, router *gin.RouterGroup) *OIDCController {
|
func NewOIDCController(config OIDCControllerConfig, oidcService *service.OIDCService, router *gin.RouterGroup, engine *gin.Engine) *OIDCController {
|
||||||
return &OIDCController{
|
return &OIDCController{
|
||||||
config: config,
|
config: config,
|
||||||
oidc: oidcService,
|
oidc: oidcService,
|
||||||
router: router,
|
router: router,
|
||||||
|
engine: engine,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +75,7 @@ func (controller *OIDCController) SetupRoutes() {
|
|||||||
oidcGroup.POST("/token", controller.Token)
|
oidcGroup.POST("/token", controller.Token)
|
||||||
oidcGroup.GET("/userinfo", controller.Userinfo)
|
oidcGroup.GET("/userinfo", controller.Userinfo)
|
||||||
oidcGroup.POST("/userinfo", controller.Userinfo)
|
oidcGroup.POST("/userinfo", controller.Userinfo)
|
||||||
|
controller.engine.POST("/authorize", controller.AuthorizePseudoPost)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (controller *OIDCController) GetClientInfo(c *gin.Context) {
|
func (controller *OIDCController) GetClientInfo(c *gin.Context) {
|
||||||
@@ -195,6 +199,18 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pseudo handler that will just redirect to get in frontend then back to backend
|
||||||
|
func (controller *OIDCController) AuthorizePseudoPost(c *gin.Context) {
|
||||||
|
body, err := io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
tlog.App.Error().Err(err).Msg("Failed to read request body")
|
||||||
|
c.Redirect(http.StatusFound, fmt.Sprintf("%s/authorize", controller.oidc.GetIssuer()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
redirectUrl := fmt.Sprintf("%s/authorize?%s", controller.oidc.GetIssuer(), body)
|
||||||
|
c.Redirect(http.StatusFound, redirectUrl)
|
||||||
|
}
|
||||||
|
|
||||||
func (controller *OIDCController) Token(c *gin.Context) {
|
func (controller *OIDCController) Token(c *gin.Context) {
|
||||||
if !controller.oidc.IsConfigured() {
|
if !controller.oidc.IsConfigured() {
|
||||||
tlog.App.Warn().Msg("OIDC not configured")
|
tlog.App.Warn().Msg("OIDC not configured")
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
|
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/config"
|
"github.com/tinyauthapp/tinyauth/internal/config"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/controller"
|
"github.com/tinyauthapp/tinyauth/internal/controller"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
|
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOIDCController(t *testing.T) {
|
func TestOIDCController(t *testing.T) {
|
||||||
@@ -846,6 +846,34 @@ func TestOIDCController(t *testing.T) {
|
|||||||
assert.Equal(t, "invalid_grant", res["error"])
|
assert.Equal(t, "invalid_grant", res["error"])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Test authorize request with POST method",
|
||||||
|
middlewares: []gin.HandlerFunc{
|
||||||
|
simpleCtx,
|
||||||
|
},
|
||||||
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
|
body := service.AuthorizeRequest{
|
||||||
|
Scope: "openid",
|
||||||
|
ResponseType: "code",
|
||||||
|
ClientID: "some-client-id",
|
||||||
|
RedirectURI: "https://test.example.com/callback",
|
||||||
|
State: "some-state",
|
||||||
|
Nonce: "some-nonce",
|
||||||
|
CodeChallenge: "some-challenge",
|
||||||
|
CodeChallengeMethod: "plain",
|
||||||
|
}
|
||||||
|
queries, err := query.Values(body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := httptest.NewRequest("POST", "/authorize", strings.NewReader(string(queries.Encode())))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
router.ServeHTTP(recorder, req)
|
||||||
|
assert.Equal(t, 302, recorder.Code)
|
||||||
|
location := recorder.Header().Get("Location")
|
||||||
|
assert.NotEmpty(t, location)
|
||||||
|
assert.Equal(t, "https://tinyauth.example.com/authorize?client_id=some-client-id&code_challenge=some-challenge&code_challenge_method=plain&nonce=some-nonce&redirect_uri=https%3A%2F%2Ftest.example.com%2Fcallback&response_type=code&scope=openid&state=some-state", location)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
app := bootstrap.NewBootstrapApp(config.Config{})
|
app := bootstrap.NewBootstrapApp(config.Config{})
|
||||||
@@ -869,7 +897,7 @@ func TestOIDCController(t *testing.T) {
|
|||||||
group := router.Group("/api")
|
group := router.Group("/api")
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
oidcController := controller.NewOIDCController(controllerCfg, oidcService, group)
|
oidcController := controller.NewOIDCController(controllerCfg, oidcService, group, router)
|
||||||
oidcController.SetupRoutes()
|
oidcController.SetupRoutes()
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func (m *UIMiddleware) Init() error {
|
|||||||
func (m *UIMiddleware) Middleware() gin.HandlerFunc {
|
func (m *UIMiddleware) Middleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
path := strings.TrimPrefix(c.Request.URL.Path, "/")
|
path := strings.TrimPrefix(c.Request.URL.Path, "/")
|
||||||
|
method := c.Request.Method
|
||||||
|
|
||||||
tlog.App.Debug().Str("path", path).Msg("path")
|
tlog.App.Debug().Str("path", path).Msg("path")
|
||||||
|
|
||||||
@@ -52,6 +53,12 @@ func (m *UIMiddleware) Middleware() gin.HandlerFunc {
|
|||||||
c.Writer.Write([]byte("User-agent: *\nDisallow: /\n"))
|
c.Writer.Write([]byte("User-agent: *\nDisallow: /\n"))
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
|
// For OIDC post authentication, we need to redirect the POST to /authorize to the backend
|
||||||
|
if method == http.MethodPost && strings.HasPrefix(path, "authorize") {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, err := fs.Stat(m.uiFs, path)
|
_, err := fs.Stat(m.uiFs, path)
|
||||||
|
|
||||||
// Enough for one authentication flow
|
// Enough for one authentication flow
|
||||||
|
|||||||
@@ -100,14 +100,15 @@ type TokenResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizeRequest struct {
|
type AuthorizeRequest struct {
|
||||||
Scope string `json:"scope" binding:"required"`
|
Scope string `json:"scope" binding:"required" url:"scope"`
|
||||||
ResponseType string `json:"response_type" binding:"required"`
|
ResponseType string `json:"response_type" binding:"required" url:"response_type"`
|
||||||
ClientID string `json:"client_id" binding:"required"`
|
ClientID string `json:"client_id" binding:"required" url:"client_id"`
|
||||||
RedirectURI string `json:"redirect_uri" binding:"required"`
|
RedirectURI string `json:"redirect_uri" binding:"required" url:"redirect_uri"`
|
||||||
State string `json:"state"`
|
State string `json:"state" url:"state"`
|
||||||
Nonce string `json:"nonce"`
|
Nonce string `json:"nonce" url:"nonce"`
|
||||||
CodeChallenge string `json:"code_challenge"`
|
CodeChallenge string `json:"code_challenge" url:"code_challenge"`
|
||||||
CodeChallengeMethod string `json:"code_challenge_method"`
|
CodeChallengeMethod string `json:"code_challenge_method" url:"code_challenge_method"`
|
||||||
|
Request string `json:"request" url:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OIDCServiceConfig struct {
|
type OIDCServiceConfig struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user