Compare commits

..

2 Commits

Author SHA1 Message Date
Stavros fb48f1eb2d feat: add swagger comments for context, health, oauth and oidc controllers 2026-07-03 23:55:22 +03:00
Stavros 33a5b859cf feat: init swagger 2026-07-03 22:59:31 +03:00
14 changed files with 3187 additions and 90 deletions
+9 -1
View File
@@ -16,7 +16,7 @@ PROD_COMPOSE := $(shell test -f "docker-compose.test.prod.yml" && echo "docker-c
.DEFAULT_GOAL := binary
.PHONY: deps clean-data clean-webui webui binary binary-linux-amd64 binary-linux-arm64 test vet test-race dev dev-infisical prod prod-infisical sql generate docker docker-distroless
.PHONY: deps clean-data clean-webui webui binary binary-linux-amd64 binary-linux-arm64 test vet test-race dev dev-infisical prod prod-infisical sql generate docker docker-distroless swagger swagger-fmt
# Deps
deps:
@@ -102,3 +102,11 @@ docker:
# Docker image distroless
docker-distroless:
docker buildx build -t tinyauthapp/tinyauth:dev-distroless --build-arg=VERSION=$(TAG_NAME) --build-arg=COMMIT_HASH=$(COMMIT_HASH) --build-arg=BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) -f Dockerfile.distroless .
# Swagger
swagger:
swag init -d ./internal -g bootstrap/router_bootstrap.go -o ./internal/swagger
# Swagger Format
swagger-fmt:
swag fmt -d ./internal -g bootstrap/router_bootstrap.go
+5
View File
@@ -62,6 +62,11 @@ export default defineConfig({
changeOrigin: true,
rewrite: (path) => path.replace(/^\/authorize/, ""),
},
"/swagger": {
target: "http://tinyauth-backend:3000/swagger",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/swagger/, ""),
}
},
allowedHosts: true,
},
+12 -2
View File
@@ -4,6 +4,7 @@ go 1.26.4
require (
charm.land/huh/v2 v2.0.3
charm.land/lipgloss/v2 v2.0.1
github.com/cenkalti/backoff/v5 v5.0.3
github.com/docker/docker v28.5.2+incompatible
github.com/gin-gonic/gin v1.12.0
@@ -19,12 +20,14 @@ require (
github.com/rs/zerolog v1.35.1
github.com/steveiliop56/ding v0.2.0
github.com/stretchr/testify v1.11.1
github.com/swaggo/swag v1.16.6
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
github.com/weppos/publicsuffix-go v0.50.3
go.uber.org/dig v1.19.0
golang.org/x/crypto v0.53.0
golang.org/x/oauth2 v0.36.0
golang.org/x/tools v0.47.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.36.2
k8s.io/client-go v0.36.2
modernc.org/sqlite v1.53.0
@@ -34,11 +37,11 @@ require (
require (
charm.land/bubbles/v2 v2.0.0 // indirect
charm.land/bubbletea/v2 v2.0.2 // indirect
charm.land/lipgloss/v2 v2.0.1 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/Azure/go-ntlmssp v0.1.1 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
@@ -82,6 +85,10 @@ require (
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
@@ -98,12 +105,14 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.20 // indirect
@@ -134,6 +143,8 @@ require (
github.com/safchain/ethtool v0.3.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/swaggo/gin-swagger v1.6.1 // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd // indirect
@@ -169,7 +180,6 @@ require (
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.2 // indirect
gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
+62
View File
@@ -20,6 +20,8 @@ github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDP
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@@ -30,6 +32,8 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
@@ -133,6 +137,7 @@ github.com/creachadair/msync v0.7.1 h1:SeZmuEBXQPe5GqV/C94ER7QIZPwtvFbeQiykzt/7u
github.com/creachadair/msync v0.7.1/go.mod h1:8CcFlLsSujfHE5wWm19uUBLHIPDAUr6LXDwneVMO008=
github.com/creachadair/taskgroup v0.13.2 h1:3KyqakBuFsm3KkXi/9XIb0QcA8tEzLHLgaoidf0MdVc=
github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9poewSb2XI1yTwD0g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -192,10 +197,17 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -297,8 +309,11 @@ github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@@ -307,6 +322,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
@@ -361,6 +379,7 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -414,6 +433,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -421,6 +441,12 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/gliderssh v0.3.4-0.20260330083525-c1389c70ff89 h1:glgVc1ZYMjwN1Q/ITWeuSQyl029uayagaR2sjsifehc=
@@ -463,6 +489,7 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
@@ -499,6 +526,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
@@ -507,27 +536,54 @@ golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo=
golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
@@ -542,13 +598,19 @@ google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
+53
View File
@@ -6,17 +6,28 @@ import (
"fmt"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/middleware"
"github.com/tinyauthapp/tinyauth/internal/model"
docs "github.com/tinyauthapp/tinyauth/internal/swagger"
"go.uber.org/dig"
"github.com/gin-gonic/gin"
)
// @title Tinyauth API
// @version development
// @description Swagger documentation for Tinyauth's API.
// @license.name AGPL-3.0
// @license.url https://github.com/tinyauthapp/tinyauth/blob/main/LICENSE
// @BasePath /
func (app *BootstrapApp) setupRouter() error {
// we don't want gin debug mode
gin.SetMode(gin.ReleaseMode)
@@ -80,6 +91,12 @@ func (app *BootstrapApp) setupRouter() error {
return fmt.Errorf("failed to provide api router group: %w", err)
}
err = app.setupSwagger()
if err != nil {
return fmt.Errorf("failed to setup swagger: %w", err)
}
controllerProvideFor := []any{
controller.NewContextController,
controller.NewOAuthController,
@@ -125,6 +142,42 @@ func (app *BootstrapApp) setupRouter() error {
return nil
}
func (app *BootstrapApp) setupSwagger() error {
appUrl, err := url.Parse(app.runtime.AppURL)
if err != nil {
return fmt.Errorf("failed to parse app url: %w", err)
}
docs.SwaggerInfo.Host = appUrl.Host
docs.SwaggerInfo.Schemes = []string{appUrl.Scheme}
docs.SwaggerInfo.Version = model.Version
type swaggerInput struct {
dig.In
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"`
}
err = app.dig.Invoke(func(i swaggerInput) {
i.RouterGroup.Use(func(c *gin.Context) {
if strings.TrimSuffix(c.Request.URL.Path, "/") == "/swagger" {
c.Redirect(http.StatusFound, "/swagger/index.html")
c.Abort()
return
}
c.Next()
})
i.RouterGroup.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
})
if err != nil {
return fmt.Errorf("failed to invoke swagger: %w", err)
}
return nil
}
// Top down
// 1. Tailscale (if tailscale.listen)
// 2. Unix socket (if server.socketPath)
+16
View File
@@ -107,6 +107,14 @@ func NewContextController(i ContextControllerInput) *ContextController {
return controller
}
// UserContext godoc
//
// @Summary User context
// @Description Get the user context
// @Tags context
// @Produce json
// @Success 200 {object} UserContextResponse
// @Router /api/context/user [get]
func (controller *ContextController) userContextHandler(c *gin.Context) {
context, err := new(model.UserContext).NewFromGin(c)
@@ -147,6 +155,14 @@ func (controller *ContextController) userContextHandler(c *gin.Context) {
c.JSON(200, userContext)
}
// AppContext godoc
//
// @Summary App context
// @Description Get the app context
// @Tags context
// @Produce json
// @Success 200 {object} AppContextResponse
// @Router /api/context/app [get]
func (controller *ContextController) appContextHandler(c *gin.Context) {
c.JSON(200, AppContextResponse{
Status: 200,
+4
View File
@@ -7,6 +7,10 @@ const (
FrontendLoginForApp FrontendLoginFor = "app"
)
type SimpleResponse struct {
Status int `json:"status"`
Message string `json:"message,omitempty"`
}
type UnauthorizedQuery struct {
Username string `url:"username"`
Resource string `url:"resource"`
+12 -3
View File
@@ -23,9 +23,18 @@ func NewHealthController(i HealthControllerInput) *HealthController {
return controller
}
// HealthCheck godoc
//
// @Summary Healthcheck
// @Description Check if the server is up and running
// @Tags health
// @Produce json
// @Success 200 {object} SimpleResponse
// @Router /api/healthz [get]
// @Router /api/healthz [head]
func (controller *HealthController) healthHandler(c *gin.Context) {
c.JSON(200, gin.H{
"status": 200,
"message": "Healthy",
c.JSON(200, SimpleResponse{
Status: 200,
Message: "OK",
})
}
+57 -26
View File
@@ -54,6 +54,27 @@ func NewOAuthController(i OAuthControllerInput) *OAuthController {
return controller
}
type OAuthURLSuccessResponse struct {
SimpleResponse
URL string `json:"url"`
}
// OAuthURL godoc
//
// @Summary OAuth URL
// @Description Get an OAuth URL for the specified provider
// @Tags oauth
// @Produce json
// @Param id path string true "Provider ID"
// @Param login_for query string false "Login for"
// @Param oidc_ticket query string false "OpenID Connect Ticket"
// @Param oidc_scope query string false "OpenID Connect Scope"
// @Param oidc_name query string false "OpenID Connect Name"
// @Param redirect_uri query string false "Redirect URI"
// @Success 200 {object} OAuthURLSuccessResponse
// @Failure 400 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Router /api/oauth/url/{id} [get]
func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
var req OAuthRequest
@@ -111,23 +132,33 @@ func (controller *OAuthController) oauthURLHandler(c *gin.Context) {
c.SetCookie(controller.runtime.OAuthSessionCookieName, sessionId, int(time.Hour.Seconds()), "/", controller.getCookieDomain(), controller.config.Auth.SecureCookie, true)
c.JSON(200, gin.H{
"status": 200,
"message": "OK",
"url": authUrl,
c.JSON(200, OAuthURLSuccessResponse{
SimpleResponse: SimpleResponse{
Status: 200,
Message: "OK",
},
URL: authUrl,
})
}
// OAuthCallback godoc
//
// @Summary OAuth Callback
// @Description Callback URL for OAuth providers
// @Tags oauth
// @Param id path string true "Provider ID"
// @Param code query string true "State"
// @Param state query string true "Code"
// @Success 302
// @Failure 302
// @Router /api/oauth/callback/{id} [get]
func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
var req OAuthRequest
err := c.BindUri(&req)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to bind URI")
c.JSON(400, gin.H{
"status": 400,
"message": "Bad Request",
})
controller.log.App.Error().Err(err).Msg("Failed to get provider ID")
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -135,7 +166,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get OAuth session cookie")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -145,7 +176,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get pending OAuth session")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -154,7 +185,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
state := c.Query("state")
if state != oauthPendingSession.State {
controller.log.App.Warn().Msg("OAuth state mismatch")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -163,7 +194,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to exchange code for token")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -171,19 +202,19 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get user info from OAuth provider")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
if user == nil {
controller.log.App.Warn().Msg("OAuth provider did not return user info")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
if user.Email == "" {
controller.log.App.Warn().Msg("OAuth provider did not return an email")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -191,13 +222,13 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get OAuth service for session")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
if svc.ID() != req.Provider {
controller.log.App.Warn().Msgf("OAuth provider mismatch: expected %s, got %s", req.Provider, svc.ID())
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -211,11 +242,11 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to encode unauthorized query")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/unauthorized?%s", controller.runtime.AppURL, queries.Encode()))
return
}
@@ -260,7 +291,7 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to create session cookie")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -273,10 +304,10 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
queries, err := query.Values(oauthPendingSession.CallbackParams)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to encode OIDC callback query")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/oidc/authorize?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/oidc/authorize?%s", controller.runtime.AppURL, queries.Encode()))
return
}
@@ -288,15 +319,15 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to encode redirect query")
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/continue?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusFound, fmt.Sprintf("%s/continue?%s", controller.runtime.AppURL, queries.Encode()))
return
}
c.Redirect(http.StatusTemporaryRedirect, controller.runtime.AppURL)
c.Redirect(http.StatusFound, controller.runtime.AppURL)
}
func (controller *OAuthController) isOidcRequest(params service.OAuthCallbackParams) bool {
+151 -57
View File
@@ -82,6 +82,15 @@ type AuthorizeCompleteRequest struct {
Ticket string `json:"ticket" binding:"required"`
}
type AuthorizeCompleteResponse struct {
SimpleResponse
RedirectURI string `json:"redirect_uri"`
}
type OIDCErrorResponse struct {
Error string `json:"error"`
}
type OIDCControllerInput struct {
dig.In
@@ -114,6 +123,36 @@ func NewOIDCController(i OIDCControllerInput) *OIDCController {
// This endpoint does **not** return a code, it handles param validation, ticket creation
// and then redirects to the frontend to handle the consent screen. It performs no destructive
// actions (like logging out an existing session)
// Authorize godoc
//
// @Summary Authorize
// @Description OpenID Connect Authorize Endpoint
// @Accept x-www-form-urlencoded
// @Tags oidc
// @Param scope query string false "OAuth scopes (space separated, must include openid)"
// @Param response_type query string false "Response type (e.g. code)"
// @Param client_id query string false "Client ID"
// @Param redirect_uri query string false "Redirect URI"
// @Param state query string false "Opaque state value returned to the client"
// @Param nonce query string false "Nonce for ID token replay protection"
// @Param code_challenge query string false "PKCE code challenge"
// @Param code_challenge_method query string false "PKCE code challenge method (S256 or plain)"
// @Param prompt query string false "Prompt parameter (none, login, consent)"
// @Param max_age query string false "Max authentication age in seconds"
// @Param scope formData string false "OAuth scopes (space separated, must include openid)"
// @Param response_type formData string false "Response type (e.g. code)"
// @Param client_id formData string false "Client ID"
// @Param redirect_uri formData string false "Redirect URI"
// @Param state formData string false "Opaque state value returned to the client"
// @Param nonce formData string false "Nonce for ID token replay protection"
// @Param code_challenge formData string false "PKCE code challenge"
// @Param code_challenge_method formData string false "PKCE code challenge method (S256 or plain)"
// @Param prompt formData string false "Prompt parameter (none, login, consent)"
// @Param max_age formData string false "Max authentication age in seconds"
// @Success 302
// @Failure 302
// @Router /authorize [get]
// @Router /authorize [post]
func (controller *OIDCController) authorize(c *gin.Context) {
if controller.oidc == nil {
controller.authorizeError(c, authorizeErrorParams{
@@ -261,6 +300,16 @@ func (controller *OIDCController) authorize(c *gin.Context) {
// The actual **internal** endpoint that actually creates the code and session.
// It is called by the frontend after the user has logged in and given consent.
// AuthorizeComplete godoc
//
// @Summary Authorize Complete
// @Description Internal endpoint for the completion of the OpenID Connect authorization flow
// @Tags oidc
// @Accept json
// @Produce json
// @Success 200 {object} AuthorizeCompleteResponse
// @Failure 500
// @Router /api/oidc/authorize-complete [post]
func (controller *OIDCController) authorizeComplete(c *gin.Context) {
if controller.oidc == nil {
// For this endpoint we return JSON errors since it's called
@@ -361,17 +410,44 @@ func (controller *OIDCController) authorizeComplete(c *gin.Context) {
return
}
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": fmt.Sprintf("%s?%s", authorizeReq.RedirectURI, queries.Encode()),
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: fmt.Sprintf("%s?%s", authorizeReq.RedirectURI, queries.Encode()),
})
}
// Token godoc
//
// @Summary Token
// @Description OpenID Connect Token Endpoint
// @Tags oidc
// @Accept x-www-form-urlencoded
// @Produce json
// @Param grant_type query string true "Grant type (authorization_code or refresh_token)"
// @Param code query string false "Authorization code (required for authorization_code grant)"
// @Param redirect_uri query string false "Redirect URI (must match the one from the authorize request)"
// @Param refresh_token query string false "Refresh token (required for refresh_token grant)"
// @Param client_id query string false "Client ID (required if not using Basic auth)"
// @Param client_secret query string false "Client secret (required for confidential clients without Basic auth)"
// @Param code_verifier query string false "PKCE code verifier (required if code_challenge was sent)"
// @Param grant_type formData string false "Grant type (authorization_code or refresh_token)"
// @Param code formData string false "Authorization code (required for authorization_code grant)"
// @Param redirect_uri formData string false "Redirect URI (must match the one from the authorize request)"
// @Param refresh_token formData string false "Refresh token (required for refresh_token grant)"
// @Param client_id formData string false "Client ID (required if not using Basic auth)"
// @Param client_secret formData string false "Client secret (required for confidential clients without Basic auth)"
// @Param code_verifier formData string false "PKCE code verifier (required if code_challenge was sent)"
// @Success 200 {object} service.TokenResponse
// @Failure 400 {object} OIDCErrorResponse
// @Failure 500 {object} OIDCErrorResponse
// @Router /oidc/token [post]
func (controller *OIDCController) Token(c *gin.Context) {
if controller.oidc == nil {
controller.log.App.Warn().Msg("Received OIDC request but OIDC server is not configured")
c.JSON(500, gin.H{
"error": "server_error",
c.JSON(500, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -381,8 +457,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
err := c.Bind(&req)
if err != nil {
controller.log.App.Warn().Err(err).Msg("Failed to bind token request")
c.JSON(400, gin.H{
"error": "invalid_request",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
@@ -390,8 +466,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
err = controller.oidc.ValidateGrantType(req.GrantType)
if err != nil {
controller.log.App.Warn().Err(err).Msg("Invalid grant type")
c.JSON(400, gin.H{
"error": err.Error(),
c.JSON(400, OIDCErrorResponse{
Error: err.Error(),
})
return
}
@@ -411,8 +487,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
if !ok {
controller.log.App.Warn().Msg("Client credentials not found in basic auth")
c.Header("www-authenticate", `Basic realm="Tinyauth OIDC Token Endpoint"`)
c.JSON(400, gin.H{
"error": "invalid_client",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_client",
})
return
}
@@ -427,16 +503,16 @@ func (controller *OIDCController) Token(c *gin.Context) {
if !ok {
controller.log.App.Warn().Str("clientId", creds.ClientID).Msg("Client not found")
c.JSON(400, gin.H{
"error": "invalid_client",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_client",
})
return
}
if client.ClientSecret != creds.ClientSecret {
controller.log.App.Warn().Str("clientId", creds.ClientID).Msg("Invalid client secret")
c.JSON(400, gin.H{
"error": "invalid_client",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_client",
})
return
}
@@ -457,15 +533,15 @@ func (controller *OIDCController) Token(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to delete session for reused code")
}
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
controller.log.App.Warn().Msg("Code not found")
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
@@ -475,8 +551,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
if entry.RedirectURI != req.RedirectURI {
controller.log.App.Warn().Msg("Redirect URI does not match")
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
@@ -485,8 +561,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
if !ok {
controller.log.App.Warn().Msg("PKCE validation failed")
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
@@ -495,8 +571,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to generate access token")
c.JSON(400, gin.H{
"error": "server_error",
c.JSON(400, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -508,23 +584,23 @@ func (controller *OIDCController) Token(c *gin.Context) {
if err != nil {
if errors.Is(err, service.ErrTokenExpired) {
controller.log.App.Warn().Msg("Refresh token expired")
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
if errors.Is(err, service.ErrInvalidClient) {
controller.log.App.Warn().Msg("Refresh token does not belong to client")
c.JSON(400, gin.H{
"error": "invalid_grant",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to refresh access token")
c.JSON(400, gin.H{
"error": "server_error",
c.JSON(400, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -538,11 +614,25 @@ func (controller *OIDCController) Token(c *gin.Context) {
c.JSON(200, tokenResponse)
}
// Userinfo godoc
//
// @Summary Userinfo
// @Description OpenID Connect Userinfo Endpoint
// @Accept x-www-form-urlencoded
// @Tags oidc
// @Param access_token formData string false "OpenID Connect Access Token"
// @Produce json
// @Success 200 {object} service.UserinfoResponse
// @Failure 400 {object} OIDCErrorResponse
// @Failure 401 {object} OIDCErrorResponse
// @Failure 500 {object} OIDCErrorResponse
// @Router /oidc/userinfo [get]
// @Router /oidc/userinfo [post]
func (controller *OIDCController) Userinfo(c *gin.Context) {
if controller.oidc == nil {
controller.log.App.Warn().Msg("Received OIDC userinfo request but OIDC server is not configured")
c.JSON(500, gin.H{
"error": "server_error",
c.JSON(500, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -554,16 +644,16 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
tokenType, bearerToken, ok := strings.Cut(authorization, " ")
if !ok {
controller.log.App.Warn().Msg("OIDC userinfo accessed with invalid authorization header")
c.JSON(401, gin.H{
"error": "invalid_request",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
if strings.ToLower(tokenType) != "bearer" {
controller.log.App.Warn().Msg("OIDC userinfo accessed with non-bearer token")
c.JSON(401, gin.H{
"error": "invalid_request",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
@@ -572,23 +662,23 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
} else if c.Request.Method == http.MethodPost {
if c.ContentType() != "application/x-www-form-urlencoded" {
controller.log.App.Warn().Msg("OIDC userinfo POST accessed with invalid content type")
c.JSON(400, gin.H{
"error": "invalid_request",
c.JSON(400, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
token = c.PostForm("access_token")
if token == "" {
controller.log.App.Warn().Msg("OIDC userinfo POST accessed without access_token")
c.JSON(401, gin.H{
"error": "invalid_request",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
} else {
controller.log.App.Warn().Msg("OIDC userinfo accessed without authorization header or POST body")
c.JSON(401, gin.H{
"error": "invalid_request",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
})
return
}
@@ -598,15 +688,15 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
if err != nil {
if errors.Is(err, service.ErrTokenNotFound) {
controller.log.App.Warn().Msg("OIDC userinfo accessed with invalid token")
c.JSON(401, gin.H{
"error": "invalid_grant",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_grant",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to get access token")
c.JSON(401, gin.H{
"error": "server_error",
c.JSON(401, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -614,8 +704,8 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
// If we don't have the openid scope, return an error
if !slices.Contains(strings.Split(entry.Scope, " "), "openid") {
controller.log.App.Warn().Msg("OIDC userinfo accessed with missing openid scope")
c.JSON(401, gin.H{
"error": "invalid_scope",
c.JSON(401, OIDCErrorResponse{
Error: "invalid_scope",
})
return
}
@@ -626,8 +716,8 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get user info")
c.JSON(401, gin.H{
"error": "server_error",
c.JSON(401, OIDCErrorResponse{
Error: "server_error",
})
return
}
@@ -662,9 +752,11 @@ func (controller *OIDCController) authorizeError(c *gin.Context, params authoriz
redirectUrl := fmt.Sprintf("%s?%s", params.callback, queries.Encode())
if params.json {
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": redirectUrl,
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: redirectUrl,
})
return
}
@@ -694,9 +786,11 @@ func (controller *OIDCController) authorizeError(c *gin.Context, params authoriz
}
if params.json {
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": redirectUrl,
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: redirectUrl,
})
return
}
+1 -1
View File
@@ -44,7 +44,7 @@ func (m *UIMiddleware) Middleware() gin.HandlerFunc {
path := strings.TrimPrefix(c.Request.URL.Path, "/")
switch strings.SplitN(path, "/", 2)[0] {
case "api", "resources", ".well-known", "authorize":
case "api", "resources", ".well-known", "authorize", "swagger":
c.Next()
return
case "robots.txt":
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+690
View File
@@ -0,0 +1,690 @@
basePath: /
definitions:
controller.ACRApp:
properties:
appUrl:
type: string
cookieDomain:
type: string
subdomainsEnabled:
type: boolean
type: object
controller.ACRAuth:
properties:
providers:
items:
$ref: '#/definitions/model.Provider'
type: array
type: object
controller.ACROAuth:
properties:
autoRedirect:
type: string
type: object
controller.ACRUI:
properties:
backgroundImage:
type: string
forgotPasswordMessage:
type: string
title:
type: string
warningsEnabled:
type: boolean
type: object
controller.AppContextResponse:
properties:
app:
$ref: '#/definitions/controller.ACRApp'
auth:
$ref: '#/definitions/controller.ACRAuth'
message:
type: string
oauth:
$ref: '#/definitions/controller.ACROAuth'
status:
type: integer
ui:
$ref: '#/definitions/controller.ACRUI'
type: object
controller.AuthorizeCompleteResponse:
properties:
message:
type: string
redirect_uri:
type: string
status:
type: integer
type: object
controller.OAuthURLSuccessResponse:
properties:
message:
type: string
status:
type: integer
url:
type: string
type: object
controller.OIDCErrorResponse:
properties:
error:
type: string
type: object
controller.SimpleResponse:
properties:
message:
type: string
status:
type: integer
type: object
controller.UCRAuth:
properties:
authenticated:
type: boolean
email:
type: string
name:
type: string
providerId:
type: string
username:
type: string
type: object
controller.UCROAuth:
properties:
active:
type: boolean
displayName:
type: string
type: object
controller.UCRTOTP:
properties:
pending:
type: boolean
type: object
controller.UCRTailscale:
properties:
nodeName:
type: string
type: object
controller.UserContextResponse:
properties:
auth:
$ref: '#/definitions/controller.UCRAuth'
message:
type: string
oauth:
$ref: '#/definitions/controller.UCROAuth'
status:
type: integer
tailscale:
$ref: '#/definitions/controller.UCRTailscale'
totp:
$ref: '#/definitions/controller.UCRTOTP'
type: object
model.AddressClaim:
properties:
country:
type: string
formatted:
type: string
locality:
type: string
postal_code:
type: string
region:
type: string
street_address:
type: string
type: object
model.Provider:
properties:
id:
type: string
name:
type: string
oauth:
type: boolean
type: object
service.TokenResponse:
properties:
access_token:
type: string
expires_in:
type: integer
id_token:
type: string
refresh_token:
type: string
scope:
type: string
token_type:
type: string
type: object
service.UserinfoResponse:
properties:
address:
$ref: '#/definitions/model.AddressClaim'
birthdate:
type: string
email:
type: string
email_verified:
type: boolean
family_name:
type: string
gender:
type: string
given_name:
type: string
groups:
items:
type: string
type: array
locale:
type: string
middle_name:
type: string
name:
type: string
nickname:
type: string
phone_number:
type: string
phone_number_verified:
type: boolean
picture:
type: string
preferred_username:
type: string
profile:
type: string
sub:
type: string
updated_at:
type: integer
website:
type: string
zoneinfo:
type: string
type: object
info:
contact: {}
description: Swagger documentation for Tinyauth's API.
license:
name: AGPL-3.0
url: https://github.com/tinyauthapp/tinyauth/blob/main/LICENSE
title: Tinyauth API
version: development
paths:
/api/context/app:
get:
description: Get the app context
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.AppContextResponse'
summary: App context
tags:
- context
/api/context/user:
get:
description: Get the user context
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.UserContextResponse'
summary: User context
tags:
- context
/api/healthz:
get:
description: Check if the server is up and running
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.SimpleResponse'
summary: Healthcheck
tags:
- health
head:
description: Check if the server is up and running
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.SimpleResponse'
summary: Healthcheck
tags:
- health
/api/oauth/callback/{id}:
get:
description: Callback URL for OAuth providers
parameters:
- description: Provider ID
in: path
name: id
required: true
type: string
- description: State
in: query
name: code
required: true
type: string
- description: Code
in: query
name: state
required: true
type: string
responses:
"302":
description: Found
summary: OAuth Callback
tags:
- oauth
/api/oauth/url/{id}:
get:
description: Get an OAuth URL for the specified provider
parameters:
- description: Provider ID
in: path
name: id
required: true
type: string
- description: Login for
in: query
name: login_for
type: string
- description: OpenID Connect Ticket
in: query
name: oidc_ticket
type: string
- description: OpenID Connect Scope
in: query
name: oidc_scope
type: string
- description: OpenID Connect Name
in: query
name: oidc_name
type: string
- description: Redirect URI
in: query
name: redirect_uri
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.OAuthURLSuccessResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/controller.SimpleResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/controller.SimpleResponse'
summary: OAuth URL
tags:
- oauth
/api/oidc/authorize-complete:
post:
consumes:
- application/json
description: Internal endpoint for the completion of the OpenID Connect authorization
flow
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controller.AuthorizeCompleteResponse'
"500":
description: Internal Server Error
summary: Authorize Complete
tags:
- oidc
/authorize:
get:
consumes:
- application/x-www-form-urlencoded
description: OpenID Connect Authorize Endpoint
parameters:
- description: OAuth scopes (space separated, must include openid)
in: query
name: scope
type: string
- description: Response type (e.g. code)
in: query
name: response_type
type: string
- description: Client ID
in: query
name: client_id
type: string
- description: Redirect URI
in: query
name: redirect_uri
type: string
- description: Opaque state value returned to the client
in: query
name: state
type: string
- description: Nonce for ID token replay protection
in: query
name: nonce
type: string
- description: PKCE code challenge
in: query
name: code_challenge
type: string
- description: PKCE code challenge method (S256 or plain)
in: query
name: code_challenge_method
type: string
- description: Prompt parameter (none, login, consent)
in: query
name: prompt
type: string
- description: Max authentication age in seconds
in: query
name: max_age
type: string
- description: OAuth scopes (space separated, must include openid)
in: formData
name: scope
type: string
- description: Response type (e.g. code)
in: formData
name: response_type
type: string
- description: Client ID
in: formData
name: client_id
type: string
- description: Redirect URI
in: formData
name: redirect_uri
type: string
- description: Opaque state value returned to the client
in: formData
name: state
type: string
- description: Nonce for ID token replay protection
in: formData
name: nonce
type: string
- description: PKCE code challenge
in: formData
name: code_challenge
type: string
- description: PKCE code challenge method (S256 or plain)
in: formData
name: code_challenge_method
type: string
- description: Prompt parameter (none, login, consent)
in: formData
name: prompt
type: string
- description: Max authentication age in seconds
in: formData
name: max_age
type: string
responses:
"302":
description: Found
summary: Authorize
tags:
- oidc
post:
consumes:
- application/x-www-form-urlencoded
description: OpenID Connect Authorize Endpoint
parameters:
- description: OAuth scopes (space separated, must include openid)
in: query
name: scope
type: string
- description: Response type (e.g. code)
in: query
name: response_type
type: string
- description: Client ID
in: query
name: client_id
type: string
- description: Redirect URI
in: query
name: redirect_uri
type: string
- description: Opaque state value returned to the client
in: query
name: state
type: string
- description: Nonce for ID token replay protection
in: query
name: nonce
type: string
- description: PKCE code challenge
in: query
name: code_challenge
type: string
- description: PKCE code challenge method (S256 or plain)
in: query
name: code_challenge_method
type: string
- description: Prompt parameter (none, login, consent)
in: query
name: prompt
type: string
- description: Max authentication age in seconds
in: query
name: max_age
type: string
- description: OAuth scopes (space separated, must include openid)
in: formData
name: scope
type: string
- description: Response type (e.g. code)
in: formData
name: response_type
type: string
- description: Client ID
in: formData
name: client_id
type: string
- description: Redirect URI
in: formData
name: redirect_uri
type: string
- description: Opaque state value returned to the client
in: formData
name: state
type: string
- description: Nonce for ID token replay protection
in: formData
name: nonce
type: string
- description: PKCE code challenge
in: formData
name: code_challenge
type: string
- description: PKCE code challenge method (S256 or plain)
in: formData
name: code_challenge_method
type: string
- description: Prompt parameter (none, login, consent)
in: formData
name: prompt
type: string
- description: Max authentication age in seconds
in: formData
name: max_age
type: string
responses:
"302":
description: Found
summary: Authorize
tags:
- oidc
/oidc/token:
post:
consumes:
- application/x-www-form-urlencoded
description: OpenID Connect Token Endpoint
parameters:
- description: Grant type (authorization_code or refresh_token)
in: query
name: grant_type
required: true
type: string
- description: Authorization code (required for authorization_code grant)
in: query
name: code
type: string
- description: Redirect URI (must match the one from the authorize request)
in: query
name: redirect_uri
type: string
- description: Refresh token (required for refresh_token grant)
in: query
name: refresh_token
type: string
- description: Client ID (required if not using Basic auth)
in: query
name: client_id
type: string
- description: Client secret (required for confidential clients without Basic
auth)
in: query
name: client_secret
type: string
- description: PKCE code verifier (required if code_challenge was sent)
in: query
name: code_verifier
type: string
- description: Grant type (authorization_code or refresh_token)
in: formData
name: grant_type
type: string
- description: Authorization code (required for authorization_code grant)
in: formData
name: code
type: string
- description: Redirect URI (must match the one from the authorize request)
in: formData
name: redirect_uri
type: string
- description: Refresh token (required for refresh_token grant)
in: formData
name: refresh_token
type: string
- description: Client ID (required if not using Basic auth)
in: formData
name: client_id
type: string
- description: Client secret (required for confidential clients without Basic
auth)
in: formData
name: client_secret
type: string
- description: PKCE code verifier (required if code_challenge was sent)
in: formData
name: code_verifier
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/service.TokenResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
summary: Token
tags:
- oidc
/oidc/userinfo:
get:
consumes:
- application/x-www-form-urlencoded
description: OpenID Connect Userinfo Endpoint
parameters:
- description: OpenID Connect Access Token
in: formData
name: access_token
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/service.UserinfoResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
summary: Userinfo
tags:
- oidc
post:
consumes:
- application/x-www-form-urlencoded
description: OpenID Connect Userinfo Endpoint
parameters:
- description: OpenID Connect Access Token
in: formData
name: access_token
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/service.UserinfoResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/controller.OIDCErrorResponse'
summary: Userinfo
tags:
- oidc
swagger: "2.0"