Compare commits

..

2 Commits

Author SHA1 Message Date
Stavros 1175e0d9eb chore: trigger ci 2026-07-06 23:07:31 +03:00
blacksmith-sh[bot] 9ba51db0d8 Migrate workflows to Blacksmith 2026-07-06 19:50:20 +00:00
26 changed files with 293 additions and 5756 deletions
-2
View File
@@ -32,8 +32,6 @@ TINYAUTH_SERVER_PORT=3000
TINYAUTH_SERVER_ADDRESS="0.0.0.0"
# The path to the Unix socket.
TINYAUTH_SERVER_SOCKETPATH=
# Enable API docs with Scalar under /scalar.
TINYAUTH_SERVER_SCALARENABLED=true
# auth config
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
ci:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+27 -35
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
create-release:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -29,7 +29,7 @@ jobs:
tag_name: nightly
generate-metadata:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: create-release
outputs:
VERSION: ${{ steps.metadata.outputs.VERSION }}
@@ -49,7 +49,7 @@ jobs:
echo "BUILD_TIMESTAMP=$(date '+%Y-%m-%dT%H:%M:%S')" >> "$GITHUB_OUTPUT"
binary-build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- create-release
- generate-metadata
@@ -94,7 +94,7 @@ jobs:
path: tinyauth-amd64
binary-build-arm:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- create-release
- generate-metadata
@@ -139,7 +139,7 @@ jobs:
path: tinyauth-arm64
image-build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- create-release
- generate-metadata
@@ -162,19 +162,17 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-amd64
cache-to: type=gha,mode=max,scope=buildkit-amd64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -196,7 +194,7 @@ jobs:
retention-days: 1
image-build-distroless:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- create-release
- generate-metadata
@@ -220,11 +218,11 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/amd64
@@ -232,8 +230,6 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-amd64
cache-to: type=gha,mode=max,scope=buildkit-distroless-amd64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -255,7 +251,7 @@ jobs:
retention-days: 1
image-build-arm:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- create-release
- generate-metadata
@@ -278,19 +274,17 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-arm64
cache-to: type=gha,mode=max,scope=buildkit-arm64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -312,7 +306,7 @@ jobs:
retention-days: 1
image-build-arm-distroless:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- create-release
- generate-metadata
@@ -336,11 +330,11 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/arm64
@@ -348,8 +342,6 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-arm64
cache-to: type=gha,mode=max,scope=buildkit-distroless-arm64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -371,7 +363,7 @@ jobs:
retention-days: 1
image-merge:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- image-build
- image-build-arm
@@ -390,8 +382,8 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Docker meta
id: meta
@@ -410,7 +402,7 @@ jobs:
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
image-merge-distroless:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- image-build-distroless
- image-build-arm-distroless
@@ -429,8 +421,8 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Docker meta
id: meta
@@ -449,7 +441,7 @@ jobs:
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
update-release:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- binary-build
- binary-build-arm
+26 -34
View File
@@ -11,7 +11,7 @@ permissions:
jobs:
generate-metadata:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
outputs:
VERSION: ${{ steps.metadata.outputs.VERSION }}
COMMIT_HASH: ${{ steps.metadata.outputs.COMMIT_HASH }}
@@ -28,7 +28,7 @@ jobs:
echo "BUILD_TIMESTAMP=$(date '+%Y-%m-%dT%H:%M:%S')" >> "$GITHUB_OUTPUT"
binary-build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- generate-metadata
steps:
@@ -70,7 +70,7 @@ jobs:
path: tinyauth-amd64
binary-build-arm:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- generate-metadata
steps:
@@ -112,7 +112,7 @@ jobs:
path: tinyauth-arm64
image-build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- generate-metadata
steps:
@@ -132,19 +132,17 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-amd64
cache-to: type=gha,mode=max,scope=buildkit-amd64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -167,7 +165,7 @@ jobs:
retention-days: 1
image-build-distroless:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- generate-metadata
- image-build
@@ -188,11 +186,11 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/amd64
@@ -200,8 +198,6 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-amd64
cache-to: type=gha,mode=max,scope=buildkit-distroless-amd64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -224,7 +220,7 @@ jobs:
retention-days: 1
image-build-arm:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- generate-metadata
steps:
@@ -244,19 +240,17 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-arm64
cache-to: type=gha,mode=max,scope=buildkit-arm64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -279,7 +273,7 @@ jobs:
retention-days: 1
image-build-arm-distroless:
runs-on: ubuntu-24.04-arm
runs-on: blacksmith-4vcpu-ubuntu-2404-arm
needs:
- generate-metadata
- image-build-arm
@@ -300,11 +294,11 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build and push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: useblacksmith/build-push-action@v2
id: build
with:
platforms: linux/arm64
@@ -312,8 +306,6 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-arm64
cache-to: type=gha,mode=max,scope=buildkit-distroless-arm64
github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -336,7 +328,7 @@ jobs:
retention-days: 1
image-merge:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- image-build
- image-build-arm
@@ -355,8 +347,8 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Docker meta
id: meta
@@ -377,7 +369,7 @@ jobs:
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
image-merge-distroless:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- image-build-distroless
- image-build-arm-distroless
@@ -396,8 +388,8 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Docker meta
id: meta
@@ -420,7 +412,7 @@ jobs:
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
update-release:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- binary-build
- binary-build-arm
+1 -1
View File
@@ -11,7 +11,7 @@ permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
security-events: write
+1 -1
View File
@@ -8,7 +8,7 @@ permissions:
jobs:
generate-sponsors:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+1 -9
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 swagger swagger-fmt
.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
# Deps
deps:
@@ -102,11 +102,3 @@ 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,11 +62,6 @@ export default defineConfig({
changeOrigin: true,
rewrite: (path) => path.replace(/^\/authorize/, ""),
},
"/scalar": {
target: "http://tinyauth-backend:3000/scalar",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/scalar/, ""),
}
},
allowedHosts: true,
},
+2 -11
View File
@@ -4,7 +4,6 @@ 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
@@ -20,15 +19,12 @@ 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/gin-scalar v0.0.0-20260704144252-280c60a0cf2c
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
@@ -38,11 +34,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
@@ -86,10 +82,6 @@ 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
@@ -106,14 +98,12 @@ 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
@@ -179,6 +169,7 @@ 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
-37
View File
@@ -20,8 +20,6 @@ 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=
@@ -32,8 +30,6 @@ 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=
@@ -137,7 +133,6 @@ 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=
@@ -197,17 +192,10 @@ 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=
@@ -309,11 +297,8 @@ 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=
@@ -322,9 +307,6 @@ 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=
@@ -379,7 +361,6 @@ 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=
@@ -433,7 +414,6 @@ 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=
@@ -441,8 +421,6 @@ 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/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=
@@ -467,8 +445,6 @@ github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tinyauthapp/gin-scalar v0.0.0-20260704144252-280c60a0cf2c h1:N91CdjSrEXoFtC+buYFz4CVcOQwAu3u7xHLlzkpuctA=
github.com/tinyauthapp/gin-scalar v0.0.0-20260704144252-280c60a0cf2c/go.mod h1:fIFpOPONYJcCZr0oJBV8kXCVxJu+0Wc73bnkITihYnY=
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298 h1:EYSb5jv8ZL/0/NVFZtY7Ejplk0QG5+3lrdL3mSrjFZQ=
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298/go.mod h1:TlUDoCF66hMqFZqoBym9bUdJ0bKAWYMir6hLJeYN5z0=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@@ -533,7 +509,6 @@ 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.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
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=
@@ -541,22 +516,16 @@ golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-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-20220817070843-5a390386f1f2/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.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
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.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.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
@@ -573,19 +542,13 @@ 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=
-45
View File
@@ -6,26 +6,17 @@ import (
"fmt"
"net"
"net/http"
"net/url"
"os"
"time"
ginScalar "github.com/tinyauthapp/gin-scalar"
"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 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)
@@ -89,14 +80,6 @@ func (app *BootstrapApp) setupRouter() error {
return fmt.Errorf("failed to provide api router group: %w", err)
}
if app.config.Server.ScalarEnabled {
err = app.setupScalar()
if err != nil {
return fmt.Errorf("failed to setup scalar: %w", err)
}
}
controllerProvideFor := []any{
controller.NewContextController,
controller.NewOAuthController,
@@ -142,34 +125,6 @@ func (app *BootstrapApp) setupRouter() error {
return nil
}
func (app *BootstrapApp) setupScalar() 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 scalarInput struct {
dig.In
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"`
}
err = app.dig.Invoke(func(i scalarInput) {
i.RouterGroup.GET("/scalar/*any", ginScalar.WrapHandler(nil))
})
if err != nil {
return fmt.Errorf("failed to invoke scalar: %w", err)
}
return nil
}
// Top down
// 1. Tailscale (if tailscale.listen)
// 2. Unix socket (if server.socketPath)
-16
View File
@@ -107,14 +107,6 @@ 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)
@@ -155,14 +147,6 @@ 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,10 +7,6 @@ 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"`
+3 -12
View File
@@ -23,18 +23,9 @@ 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, SimpleResponse{
Status: 200,
Message: "Healthy",
c.JSON(200, gin.H{
"status": 200,
"message": "Healthy",
})
}
@@ -23,9 +23,9 @@ func TestHealthController(t *testing.T) {
path: "/api/healthz",
method: "GET",
expected: func() string {
expectedHealthResponse := SimpleResponse{
Status: 200,
Message: "Healthy",
expectedHealthResponse := map[string]any{
"status": 200,
"message": "Healthy",
}
bytes, err := json.Marshal(expectedHealthResponse)
require.NoError(t, err)
@@ -37,9 +37,9 @@ func TestHealthController(t *testing.T) {
path: "/api/healthz",
method: "HEAD",
expected: func() string {
expectedHealthResponse := SimpleResponse{
Status: 200,
Message: "Healthy",
expectedHealthResponse := map[string]any{
"status": 200,
"message": "Healthy",
}
bytes, err := json.Marshal(expectedHealthResponse)
require.NoError(t, err)
+26 -57
View File
@@ -54,27 +54,6 @@ 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
@@ -132,33 +111,23 @@ 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, OAuthURLSuccessResponse{
SimpleResponse: SimpleResponse{
Status: 200,
Message: "OK",
},
URL: authUrl,
c.JSON(200, gin.H{
"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 get provider ID")
c.Redirect(http.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
controller.log.App.Error().Err(err).Msg("Failed to bind URI")
c.JSON(400, gin.H{
"status": 400,
"message": "Bad Request",
})
return
}
@@ -166,7 +135,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -176,7 +145,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -185,7 +154,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -194,7 +163,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -202,19 +171,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, 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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, 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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -222,13 +191,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, 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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -242,11 +211,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusFound, fmt.Sprintf("%s/unauthorized?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", controller.runtime.AppURL, queries.Encode()))
return
}
@@ -291,7 +260,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
@@ -304,10 +273,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusFound, fmt.Sprintf("%s/oidc/authorize?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/oidc/authorize?%s", controller.runtime.AppURL, queries.Encode()))
return
}
@@ -319,15 +288,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.StatusFound, fmt.Sprintf("%s/error", controller.runtime.AppURL))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
return
}
c.Redirect(http.StatusFound, fmt.Sprintf("%s/continue?%s", controller.runtime.AppURL, queries.Encode()))
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/continue?%s", controller.runtime.AppURL, queries.Encode()))
return
}
c.Redirect(http.StatusFound, controller.runtime.AppURL)
c.Redirect(http.StatusTemporaryRedirect, controller.runtime.AppURL)
}
func (controller *OAuthController) isOidcRequest(params service.OAuthCallbackParams) bool {
+57 -151
View File
@@ -82,15 +82,6 @@ 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
@@ -123,36 +114,6 @@ 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{
@@ -300,16 +261,6 @@ 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
@@ -410,44 +361,17 @@ func (controller *OIDCController) authorizeComplete(c *gin.Context) {
return
}
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: fmt.Sprintf("%s?%s", authorizeReq.RedirectURI, queries.Encode()),
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": 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, OIDCErrorResponse{
Error: "server_error",
c.JSON(500, gin.H{
"error": "server_error",
})
return
}
@@ -457,8 +381,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, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(400, gin.H{
"error": "invalid_request",
})
return
}
@@ -466,8 +390,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, OIDCErrorResponse{
Error: err.Error(),
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}
@@ -487,8 +411,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, OIDCErrorResponse{
Error: "invalid_client",
c.JSON(400, gin.H{
"error": "invalid_client",
})
return
}
@@ -503,16 +427,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, OIDCErrorResponse{
Error: "invalid_client",
c.JSON(400, gin.H{
"error": "invalid_client",
})
return
}
if client.ClientSecret != creds.ClientSecret {
controller.log.App.Warn().Str("clientId", creds.ClientID).Msg("Invalid client secret")
c.JSON(400, OIDCErrorResponse{
Error: "invalid_client",
c.JSON(400, gin.H{
"error": "invalid_client",
})
return
}
@@ -533,15 +457,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, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
controller.log.App.Warn().Msg("Code not found")
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
@@ -551,8 +475,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, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
@@ -561,8 +485,8 @@ func (controller *OIDCController) Token(c *gin.Context) {
if !ok {
controller.log.App.Warn().Msg("PKCE validation failed")
c.JSON(400, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
@@ -571,8 +495,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, OIDCErrorResponse{
Error: "server_error",
c.JSON(400, gin.H{
"error": "server_error",
})
return
}
@@ -584,23 +508,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, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"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, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to refresh access token")
c.JSON(400, OIDCErrorResponse{
Error: "server_error",
c.JSON(400, gin.H{
"error": "server_error",
})
return
}
@@ -614,25 +538,11 @@ 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, OIDCErrorResponse{
Error: "server_error",
c.JSON(500, gin.H{
"error": "server_error",
})
return
}
@@ -644,16 +554,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, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
if strings.ToLower(tokenType) != "bearer" {
controller.log.App.Warn().Msg("OIDC userinfo accessed with non-bearer token")
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
@@ -662,23 +572,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, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(400, gin.H{
"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, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
} else {
controller.log.App.Warn().Msg("OIDC userinfo accessed without authorization header or POST body")
c.JSON(401, OIDCErrorResponse{
Error: "invalid_request",
c.JSON(401, gin.H{
"error": "invalid_request",
})
return
}
@@ -688,15 +598,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, OIDCErrorResponse{
Error: "invalid_grant",
c.JSON(401, gin.H{
"error": "invalid_grant",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to get access token")
c.JSON(401, OIDCErrorResponse{
Error: "server_error",
c.JSON(401, gin.H{
"error": "server_error",
})
return
}
@@ -704,8 +614,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, OIDCErrorResponse{
Error: "invalid_scope",
c.JSON(401, gin.H{
"error": "invalid_scope",
})
return
}
@@ -716,8 +626,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, OIDCErrorResponse{
Error: "server_error",
c.JSON(401, gin.H{
"error": "server_error",
})
return
}
@@ -752,11 +662,9 @@ func (controller *OIDCController) authorizeError(c *gin.Context, params authoriz
redirectUrl := fmt.Sprintf("%s?%s", params.callback, queries.Encode())
if params.json {
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: redirectUrl,
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": redirectUrl,
})
return
}
@@ -786,11 +694,9 @@ func (controller *OIDCController) authorizeError(c *gin.Context, params authoriz
}
if params.json {
c.JSON(200, AuthorizeCompleteResponse{
SimpleResponse: SimpleResponse{
Status: 200,
},
RedirectURI: redirectUrl,
c.JSON(200, gin.H{
"status": 200,
"redirect_uri": redirectUrl,
})
return
}
+27 -50
View File
@@ -86,38 +86,15 @@ func NewProxyController(i ProxyControllerInput) *ProxyController {
return controller
}
// Proxy godoc
//
// @Summary Proxy
// @Description Forward-Auth Proxy Endpoint
// @Tags forward-auth
// @Produce json
// @Param proxy path string true "Proxy Name"
// @Success 200 {object} SimpleResponse
// @Failure 302
// @Failure 400 {object} SimpleResponse
// @Failure 401 {object} SimpleResponse
// @Failure 403 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Router /api/auth/traefik [get]
// @Router /api/auth/caddy [get]
// @Router /api/auth/nginx [get]
// @Router /api/auth/envoy [get]
// @Router /api/auth/envoy [post]
// @Router /api/auth/envoy [head]
// @Router /api/auth/envoy [put]
// @Router /api/auth/envoy [patch]
// @Router /api/auth/envoy [delete]
// @Router /api/auth/envoy [options]
func (controller *ProxyController) proxyHandler(c *gin.Context) {
// Load proxy context based on the request type
proxyCtx, err := controller.getProxyContext(c)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get proxy context from request")
c.JSON(400, SimpleResponse{
Status: 400,
Message: "Bad request",
c.JSON(400, gin.H{
"status": 400,
"message": "Bad request",
})
return
}
@@ -141,9 +118,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if controller.policyEngine.Evaluate(service.RuleIPBypassed, aclsCtx) {
controller.setHeaders(c, acls)
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Authenticated",
c.JSON(200, gin.H{
"status": 200,
"message": "Authenticated",
})
return
}
@@ -151,9 +128,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if controller.policyEngine.Evaluate(service.RuleAuthEnabled, aclsCtx) {
controller.log.App.Debug().Msg("Authentication is disabled for this resource, allowing access without authentication")
controller.setHeaders(c, acls)
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Authenticated",
c.JSON(200, gin.H{
"status": 200,
"message": "Authenticated",
})
return
}
@@ -174,9 +151,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if !controller.useBrowserResponse(proxyCtx) {
c.Header("x-tinyauth-location", redirectURL)
c.JSON(403, SimpleResponse{
Status: 403,
Message: "Forbidden",
c.JSON(403, gin.H{
"status": 403,
"message": "Forbidden",
})
return
}
@@ -223,9 +200,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if !controller.useBrowserResponse(proxyCtx) {
c.Header("x-tinyauth-location", redirectURL)
c.JSON(403, SimpleResponse{
Status: 403,
Message: "Forbidden",
c.JSON(403, gin.H{
"status": 403,
"message": "Forbidden",
})
return
}
@@ -267,9 +244,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if !controller.useBrowserResponse(proxyCtx) {
c.Header("x-tinyauth-location", redirectURL)
c.JSON(403, SimpleResponse{
Status: 403,
Message: "Forbidden",
c.JSON(403, gin.H{
"status": 403,
"message": "Forbidden",
})
return
}
@@ -294,9 +271,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
controller.setHeaders(c, acls)
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Authenticated",
c.JSON(200, gin.H{
"status": 200,
"message": "Authenticated",
})
return
}
@@ -316,9 +293,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if !controller.useBrowserResponse(proxyCtx) {
c.Header("x-tinyauth-location", redirectURL)
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -352,9 +329,9 @@ func (controller *ProxyController) handleError(c *gin.Context, proxyCtx ProxyCon
if !controller.useBrowserResponse(proxyCtx) {
c.Header("x-tinyauth-location", redirectURL)
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
+6 -16
View File
@@ -33,28 +33,18 @@ func NewResourcesController(i ResourcesControllerInput) *ResourcesController {
return controller
}
// Resources godoc
//
// @Summary Resources Endpoint
// @Description Get a resource by file name
// @Tags resources
// @Param resource path string true "Resource Name"
// @Success 200
// @Failure 404 {object} SimpleResponse
// @Failure 403 {object} SimpleResponse
// @Router /resources/{resource} [get]
func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
if controller.config.Resources.Path == "" {
c.JSON(404, SimpleResponse{
Status: 404,
Message: "Resource not found",
c.JSON(404, gin.H{
"status": 404,
"message": "Resource not found",
})
return
}
if !controller.config.Resources.Enabled {
c.JSON(403, SimpleResponse{
Status: 403,
Message: "Resources are disabled",
c.JSON(403, gin.H{
"status": 403,
"message": "Resources are disabled",
})
return
}
+85 -139
View File
@@ -32,11 +32,6 @@ type UserController struct {
auth *service.AuthService
}
type TotpPendingResponse struct {
SimpleResponse
TotpPending bool `json:"totpPending"`
}
type UserControllerInput struct {
dig.In
@@ -62,29 +57,15 @@ func NewUserController(i UserControllerInput) *UserController {
return controller
}
// Login godoc
//
// @Summary Login
// @Description Login Endpoint
// @Tags accounts
// @Accept json
// @Produce json
// @Success 200 {object} SimpleResponse
// @Success 200 {object} TotpPendingResponse
// @Failure 400 {object} SimpleResponse
// @Failure 401 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Failure 429 {object} SimpleResponse
// @Router /api/user/login [post]
func (controller *UserController) loginHandler(c *gin.Context) {
var req LoginRequest
err := c.ShouldBindJSON(&req)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to bind JSON")
c.JSON(400, SimpleResponse{
Status: 400,
Message: "Bad Request",
c.JSON(400, gin.H{
"status": 400,
"message": "Bad Request",
})
return
}
@@ -98,9 +79,9 @@ func (controller *UserController) loginHandler(c *gin.Context) {
controller.log.AuditLoginFailure(req.Username, "local", c.ClientIP(), "account locked")
c.Writer.Header().Add("x-tinyauth-lock-locked", "true")
c.Writer.Header().Add("x-tinyauth-lock-reset", time.Now().Add(time.Duration(remaining)*time.Second).Format(time.RFC3339))
c.JSON(429, SimpleResponse{
Status: 429,
Message: fmt.Sprintf("Too many failed login attempts. Try again in %d seconds", remaining),
c.JSON(429, gin.H{
"status": 429,
"message": fmt.Sprintf("Too many failed login attempts. Try again in %d seconds", remaining),
})
return
}
@@ -112,16 +93,16 @@ func (controller *UserController) loginHandler(c *gin.Context) {
controller.log.App.Warn().Str("username", req.Username).Msg("User not found during login attempt")
controller.auth.RecordLoginAttempt(req.Username, false)
controller.log.AuditLoginFailure(req.Username, "unknown", c.ClientIP(), "user not found")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Error searching for user during login attempt")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -134,9 +115,9 @@ func (controller *UserController) loginHandler(c *gin.Context) {
} else {
controller.log.AuditLoginFailure(req.Username, "ldap", c.ClientIP(), "invalid password")
}
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -148,9 +129,9 @@ func (controller *UserController) loginHandler(c *gin.Context) {
if localUser == nil {
controller.log.App.Error().Str("username", req.Username).Msg("Local user not found after successful password verification")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -178,21 +159,19 @@ func (controller *UserController) loginHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Failed to create pending TOTP session")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
http.SetCookie(c.Writer, cookie)
c.JSON(200, TotpPendingResponse{
SimpleResponse: SimpleResponse{
Status: 200,
Message: "TOTP required",
},
TotpPending: true,
c.JSON(200, gin.H{
"status": 200,
"message": "TOTP required",
"totpPending": true,
})
return
}
@@ -225,9 +204,9 @@ func (controller *UserController) loginHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Failed to create session cookie after successful login")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -244,21 +223,12 @@ func (controller *UserController) loginHandler(c *gin.Context) {
controller.auth.RecordLoginAttempt(req.Username, true)
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Login successful",
c.JSON(200, gin.H{
"status": 200,
"message": "Login successful",
})
}
// Logout godoc
//
// @Summary Logout
// @Description Logout Endpoint
// @Tags accounts
// @Produce json
// @Success 200 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Router /api/user/logout [post]
func (controller *UserController) logoutHandler(c *gin.Context) {
controller.log.App.Debug().Msg("Logout attempt")
@@ -267,16 +237,16 @@ func (controller *UserController) logoutHandler(c *gin.Context) {
if err != nil {
if errors.Is(err, http.ErrNoCookie) {
controller.log.App.Warn().Msg("Logout attempt without session cookie, treating as successful logout")
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Logout successful",
c.JSON(200, gin.H{
"status": 200,
"message": "Logout successful",
})
return
}
controller.log.App.Error().Err(err).Msg("Error retrieving session cookie on logout")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -285,9 +255,9 @@ func (controller *UserController) logoutHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Msg("Error deleting session on logout")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -303,34 +273,21 @@ func (controller *UserController) logoutHandler(c *gin.Context) {
http.SetCookie(c.Writer, cookie)
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Logout successful",
c.JSON(200, gin.H{
"status": 200,
"message": "Logout successful",
})
}
// TOTP godoc
//
// @Summary TOTP
// @Description TOTP Endpoint
// @Tags accounts
// @Accept json
// @Produce json
// @Success 200 {object} SimpleResponse
// @Failure 400 {object} SimpleResponse
// @Failure 401 {object} SimpleResponse
// @Failure 429 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Router /api/user/totp [post]
func (controller *UserController) totpHandler(c *gin.Context) {
var req TotpRequest
err := c.ShouldBindJSON(&req)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to bind JSON for TOTP verification")
c.JSON(400, SimpleResponse{
Status: 400,
Message: "Bad Request",
c.JSON(400, gin.H{
"status": 400,
"message": "Bad Request",
})
return
}
@@ -340,25 +297,25 @@ func (controller *UserController) totpHandler(c *gin.Context) {
if err != nil {
if errors.Is(err, model.ErrUserContextNotFound) {
controller.log.App.Warn().Msg("TOTP verification attempt without user context")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to create user context from request for TOTP verification")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
if !context.TOTPPending() {
controller.log.App.Warn().Str("username", context.GetUsername()).Msg("TOTP verification attempt without pending TOTP session")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -372,9 +329,9 @@ func (controller *UserController) totpHandler(c *gin.Context) {
controller.log.AuditLoginFailure(context.GetUsername(), "local", c.ClientIP(), "account locked")
c.Writer.Header().Add("x-tinyauth-lock-locked", "true")
c.Writer.Header().Add("x-tinyauth-lock-reset", time.Now().Add(time.Duration(remaining)*time.Second).Format(time.RFC3339))
c.JSON(429, SimpleResponse{
Status: 429,
Message: fmt.Sprintf("Too many failed TOTP attempts. Try again in %d seconds", remaining),
c.JSON(429, gin.H{
"status": 429,
"message": fmt.Sprintf("Too many failed TOTP attempts. Try again in %d seconds", remaining),
})
return
}
@@ -383,9 +340,9 @@ func (controller *UserController) totpHandler(c *gin.Context) {
if user == nil {
controller.log.App.Error().Str("username", context.GetUsername()).Msg("Local user not found during TOTP verification")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -396,9 +353,9 @@ func (controller *UserController) totpHandler(c *gin.Context) {
controller.log.App.Warn().Str("username", context.GetUsername()).Msg("Invalid TOTP code during verification attempt")
controller.auth.RecordLoginAttempt(context.GetUsername(), false)
controller.log.AuditLoginFailure(context.GetUsername(), "local", c.ClientIP(), "invalid TOTP code")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -434,9 +391,9 @@ func (controller *UserController) totpHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Str("username", context.GetUsername()).Msg("Failed to create session cookie after successful TOTP verification")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -446,48 +403,37 @@ func (controller *UserController) totpHandler(c *gin.Context) {
controller.log.App.Info().Str("username", context.GetUsername()).Msg("TOTP verification successful, login complete")
controller.log.AuditLoginSuccess(context.GetUsername(), "local", c.ClientIP())
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Login successful",
c.JSON(200, gin.H{
"status": 200,
"message": "Login successful",
})
}
// Tailscale godoc
//
// @Summary Tailscale
// @Description Tailscale Auth Endpoint (Experimental)
// @Tags accounts
// @Accept json
// @Produce json
// @Success 200 {object} SimpleResponse
// @Failure 401 {object} SimpleResponse
// @Failure 500 {object} SimpleResponse
// @Router /api/user/tailscale [post]
func (controller *UserController) tailscaleHandler(c *gin.Context) {
context, err := new(model.UserContext).NewFromGin(c)
if err != nil {
if errors.Is(err, model.ErrUserContextNotFound) {
controller.log.App.Warn().Msg("Tailscale login attempt without user context")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to create user context from request")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
if context.Tailscale == nil {
controller.log.App.Warn().Msg("Tailscale login attempt without Tailscale context")
c.JSON(401, SimpleResponse{
Status: 401,
Message: "Unauthorized",
c.JSON(401, gin.H{
"status": 401,
"message": "Unauthorized",
})
return
}
@@ -503,9 +449,9 @@ func (controller *UserController) tailscaleHandler(c *gin.Context) {
if err != nil {
controller.log.App.Error().Err(err).Str("username", context.GetUsername()).Msg("Failed to create session cookie after successful Tailscale login")
c.JSON(500, SimpleResponse{
Status: 500,
Message: "Internal Server Error",
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
@@ -515,8 +461,8 @@ func (controller *UserController) tailscaleHandler(c *gin.Context) {
controller.log.App.Info().Str("username", context.GetUsername()).Msg("Tailscale login successful, login complete")
controller.log.AuditLoginSuccess(context.GetUsername(), "tailscale", c.ClientIP())
c.JSON(200, SimpleResponse{
Status: 200,
Message: "Login successful",
c.JSON(200, gin.H{
"status": 200,
"message": "Login successful",
})
}
+18 -47
View File
@@ -58,27 +58,18 @@ func NewWellKnownController(i WellKnownControllerInput) *WellKnownController {
oidc: i.OIDCService,
}
i.RouterGroup.GET("/.well-known/openid-configuration", controller.openIDConnectConfiguration)
i.RouterGroup.GET("/.well-known/jwks.json", controller.jwks)
i.RouterGroup.GET("/.well-known/webfinger", controller.webFinger)
i.RouterGroup.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration)
i.RouterGroup.GET("/.well-known/jwks.json", controller.JWKS)
i.RouterGroup.GET("/.well-known/webfinger", controller.WebFinger)
return controller
}
// OpenIDConnectConfiguration godoc
//
// @Summary OpenID Connect Configuration
// @Description OpenID Connect Configuration Discovery Endpoint
// @Tags well-known
// @Produce json
// @Success 200 {object} OpenIDConnectConfiguration
// @Failure 500 {object} SimpleResponse
// @Router /.well-known/openid-configuration [get]
func (controller *WellKnownController) openIDConnectConfiguration(c *gin.Context) {
func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context) {
if controller.oidc == nil {
c.JSON(500, SimpleResponse{
Status: 500,
Message: "OIDC service not configured",
c.JSON(500, gin.H{
"status": 500,
"message": "OIDC service not configured",
})
return
}
@@ -103,20 +94,11 @@ func (controller *WellKnownController) openIDConnectConfiguration(c *gin.Context
})
}
// JWKS godoc
//
// @Summary JWKS
// @Description JWKS Endpoint
// @Tags well-known
// @Produce json
// @Success 200
// @Failure 500 {object} SimpleResponse
// @Router /.well-known/jwks.json [get]
func (controller *WellKnownController) jwks(c *gin.Context) {
func (controller *WellKnownController) JWKS(c *gin.Context) {
if controller.oidc == nil {
c.JSON(500, SimpleResponse{
Status: 500,
Message: "OIDC service not configured",
c.JSON(500, gin.H{
"status": 500,
"message": "OIDC service not configured",
})
return
}
@@ -124,9 +106,9 @@ func (controller *WellKnownController) jwks(c *gin.Context) {
jwks, err := controller.oidc.GetJWK()
if err != nil {
c.JSON(500, SimpleResponse{
Status: 500,
Message: "failed to get JWK",
c.JSON(500, gin.H{
"status": 500,
"message": "failed to get JWK",
})
return
}
@@ -140,27 +122,16 @@ func (controller *WellKnownController) jwks(c *gin.Context) {
c.Status(http.StatusOK)
}
// WebFinger godoc
//
// @Summary WebFinger
// @Description WebFinger Endpoint
// @Tags well-known
// @Produce json
// @Param resource query string true "Resource"
// @Param rel query string false "Rel"
// @Success 200 {object} WebfingerResponse
// @Failure 400 {object} SimpleResponse
// @Router /.well-known/webfinger [get]
func (controller *WellKnownController) webFinger(c *gin.Context) {
func (controller *WellKnownController) WebFinger(c *gin.Context) {
c.Header("Content-Type", "application/jrd+json")
c.Header("Access-Control-Allow-Origin", "*")
resource := c.Query("resource")
if !controller.validateWebFingerResource(resource) {
c.JSON(400, SimpleResponse{
Status: 400,
Message: "invalid resource",
c.JSON(400, gin.H{
"status": 400,
"message": "invalid resource",
})
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", "scalar":
case "api", "resources", ".well-known", "authorize":
c.Next()
return
case "robots.txt":
+5 -7
View File
@@ -34,9 +34,8 @@ func NewDefaultConfiguration(runtimeEnv RuntimeEnv) *Config {
Path: "./resources",
},
Server: ServerConfig{
Port: 3000,
Address: "0.0.0.0",
ScalarEnabled: true,
Port: 3000,
Address: "0.0.0.0",
},
Auth: AuthConfig{
SubdomainsEnabled: true,
@@ -135,10 +134,9 @@ type ResourcesConfig struct {
}
type ServerConfig struct {
Port int `description:"The port on which the server listens." yaml:"port,omitempty"`
Address string `description:"The address on which the server listens." yaml:"address,omitempty"`
SocketPath string `description:"The path to the Unix socket." yaml:"socketPath,omitempty"`
ScalarEnabled bool `description:"Enable API docs with Scalar under /scalar." yaml:"scalarEnabled,omitempty"`
Port int `description:"The port on which the server listens." yaml:"port,omitempty"`
Address string `description:"The address on which the server listens." yaml:"address,omitempty"`
SocketPath string `description:"The path to the Unix socket." yaml:"socketPath,omitempty"`
}
type AuthConfig struct {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff