Compare commits

..

1 Commits

Author SHA1 Message Date
Stavros e15e92c45f feat: preserve login params in forgot password screen 2026-04-26 17:50:38 +03:00
77 changed files with 314 additions and 920 deletions
+1 -2
View File
@@ -3,8 +3,7 @@ name: Bug report
about: Create a report to help improve Tinyauth
title: "[BUG]"
labels: bug
assignees:
- steveiliop56
assignees: steveiliop56
---
+1 -2
View File
@@ -3,8 +3,7 @@ name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees:
- steveiliop56
assignees: steveiliop56
---
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Setup bun
uses: oven-sh/setup-bun@v2
+16 -16
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Delete old release
run: gh release delete --cleanup-tag --yes nightly || echo release not found
@@ -33,7 +33,7 @@ jobs:
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -51,7 +51,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -80,12 +80,12 @@ jobs:
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
go build -ldflags "-s -w -X github.com/tinyauthapp/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64 ./cmd/tinyauth
go build -ldflags "-s -w -X github.com/steveiliop56/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64 ./cmd/tinyauth
env:
CGO_ENABLED: 0
- name: Upload artifact
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: tinyauth-amd64
path: tinyauth-amd64
@@ -97,7 +97,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -126,12 +126,12 @@ jobs:
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
go build -ldflags "-s -w -X github.com/tinyauthapp/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64 ./cmd/tinyauth
go build -ldflags "-s -w -X github.com/steveiliop56/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64 ./cmd/tinyauth
env:
CGO_ENABLED: 0
- name: Upload artifact
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: tinyauth-arm64
path: tinyauth-arm64
@@ -143,7 +143,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -186,7 +186,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-linux-amd64
path: ${{ runner.temp }}/digests/*
@@ -201,7 +201,7 @@ jobs:
- image-build
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -245,7 +245,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-distroless-linux-amd64
path: ${{ runner.temp }}/digests/*
@@ -259,7 +259,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -302,7 +302,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-linux-arm64
path: ${{ runner.temp }}/digests/*
@@ -317,7 +317,7 @@ jobs:
- image-build-arm
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
with:
ref: nightly
@@ -361,7 +361,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-distroless-linux-arm64
path: ${{ runner.temp }}/digests/*
+15 -15
View File
@@ -14,7 +14,7 @@ jobs:
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Generate metadata
id: metadata
@@ -29,7 +29,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Install bun
uses: oven-sh/setup-bun@v2
@@ -56,12 +56,12 @@ jobs:
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
go build -ldflags "-s -w -X github.com/tinyauthapp/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64 ./cmd/tinyauth
go build -ldflags "-s -w -X github.com/steveiliop56/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64 ./cmd/tinyauth
env:
CGO_ENABLED: 0
- name: Upload artifact
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: tinyauth-amd64
path: tinyauth-amd64
@@ -72,7 +72,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Install bun
uses: oven-sh/setup-bun@v2
@@ -99,12 +99,12 @@ jobs:
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
go build -ldflags "-s -w -X github.com/tinyauthapp/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64 ./cmd/tinyauth
go build -ldflags "-s -w -X github.com/steveiliop56/tinyauth/internal/config.Version=${{ needs.generate-metadata.outputs.VERSION }} -X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64 ./cmd/tinyauth
env:
CGO_ENABLED: 0
- name: Upload artifact
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: tinyauth-arm64
path: tinyauth-arm64
@@ -115,7 +115,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Docker meta
id: meta
@@ -156,7 +156,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-linux-amd64
path: ${{ runner.temp }}/digests/*
@@ -170,7 +170,7 @@ jobs:
- image-build
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Docker meta
id: meta
@@ -212,7 +212,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-distroless-linux-amd64
path: ${{ runner.temp }}/digests/*
@@ -225,7 +225,7 @@ jobs:
- generate-metadata
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Docker meta
id: meta
@@ -266,7 +266,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-linux-arm64
path: ${{ runner.temp }}/digests/*
@@ -280,7 +280,7 @@ jobs:
- image-build-arm
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Docker meta
id: meta
@@ -322,7 +322,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7.0.1
uses: actions/upload-artifact@v7
with:
name: digests-distroless-linux-arm64
path: ${{ runner.temp }}/digests/*
+4 -4
View File
@@ -19,25 +19,25 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
uses: actions/checkout@v6
- name: Generate Sponsors
uses: JamesIves/github-sponsors-readme-action@v1
-27
View File
@@ -1,27 +0,0 @@
# AI Usage Policy
> [!NOTE]
> By Tinyauth, we refer to the entire Tinyauth ([tinyauthapp](https://github.com/tinyauthapp)) organization and all of the repositories under it.
## How we utilize AI in Tinyauth
In Tinyauth, we see AI as another tool designed to help developers accelerate their work, ***not*** as something that should be doing the development for them. The ways we utilize large language models in Tinyauth are the following:
- **Pull request reviews**: We utilize [CodeRabbit](https://www.coderabbit.ai/) for reviews in our pull requests which helps us find and fix issues faster, minimizing the time maintainers have to spend reviewing.
- **Documentation and Issues**: We use [Dosu](https://dosu.dev/) to help resolve duplicate issues faster and automatically update our documentation based on changes in the code base.
- **In-Line Suggestions**: GitHub's [Copilot](https://github.com/features/copilot) is partially used to fill in boilerplate code through in-line suggestions.
## How we expect the community to use AI
We expect the Tinyauth community to use AI as a tool for faster development and not as a way to implement entire features through prompts. For this reason, the following guidelines are in place for AI generated content:
- **All usage must be clearly labeled**: Any content generated by AI must be clearly labeled as such. In the case that a pull request is clearly generated by AI and the author fails to disclose its use, it will be rejected.
- **All generated content should be completely understood by the account holder**: The human who utilized the large language model to generate content must have a thorough understanding of it. This includes understanding the resulting output to the full extent and being able to explain it in detail in case it's needed.
- **Automated systems are not allowed**: All forms of automated systems that utilize large language models to generate content without human oversight are forbidden. This includes any system that generates content without a human being directly involved in the process like for example with OpenClaw.
- **No generated content other than text is allowed**: Images, videos, audio and any other form of content generated by AI other than text is not allowed in Tinyauth.
- **AI pull requests are not guaranteed to be accepted or prioritized**: Any pull request that contains AI generated content is not guaranteed to be accepted and/or prioritized. The maintainers are responsible for reviewing all pull requests and determining whether or not they meet the standards of the project. AI generated content will be reviewed with the same standards as any other content, and may be rejected if it does not meet those standards.
- **Large generated pull requests will be rejected**: Any pull request that contains a large amount of generated content will be rejected. This is because it is difficult for the maintainers to review and verify large amounts of generated content.
## Tinyauth is developed by humans, for humans
Please remember that Tinyauth is developed by humans. While AI can be a useful tool for **assisting** in the development process, it should not be used in place of the human brain. Moving forward, we are committed to ensuring that most, if not all the content in Tinyauth is created and reviewed by humans, and that AI is only used as a tool to assist in the development process.
+1 -4
View File
@@ -2,9 +2,6 @@
Contributing to Tinyauth is straightforward. Follow the steps below to set up a development server.
> [!NOTE]
> If you are using large language models to contribute to the project, please ensure that you have read and understood the [AI Policy](AI_POLICY.md).
## Requirements
- Bun
@@ -18,7 +15,7 @@ Contributing to Tinyauth is straightforward. Follow the steps below to set up a
Start by cloning the repository:
```sh
git clone https://github.com/tinyauthapp/tinyauth
git clone https://github.com/steveiliop56/tinyauth
cd tinyauth
```
+4 -4
View File
@@ -1,5 +1,5 @@
# Site builder
FROM oven/bun:1.3.13-alpine AS frontend-builder
FROM oven/bun:1.3.12-alpine AS frontend-builder
WORKDIR /frontend
@@ -38,9 +38,9 @@ COPY ./internal ./internal
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
RUN CGO_ENABLED=0 go build -ldflags "-s -w \
-X github.com/tinyauthapp/tinyauth/internal/config.Version=${VERSION} \
-X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
-X github.com/steveiliop56/tinyauth/internal/config.Version=${VERSION} \
-X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
# Runner
FROM alpine:3.23 AS runner
+4 -4
View File
@@ -1,5 +1,5 @@
# Site builder
FROM oven/bun:1.3.13-alpine AS frontend-builder
FROM oven/bun:1.3.12-alpine AS frontend-builder
WORKDIR /frontend
@@ -40,9 +40,9 @@ COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
RUN mkdir -p data
RUN CGO_ENABLED=0 go build -ldflags "-s -w \
-X github.com/tinyauthapp/tinyauth/internal/config.Version=${VERSION} \
-X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
-X github.com/steveiliop56/tinyauth/internal/config.Version=${VERSION} \
-X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
# Runner
FROM gcr.io/distroless/static-debian12:latest AS runner
+3 -3
View File
@@ -37,9 +37,9 @@ webui: clean-webui
# Build the binary
binary: webui
CGO_ENABLED=$(CGO_ENABLED) go build -ldflags "-s -w \
-X github.com/tinyauthapp/tinyauth/internal/config.Version=${TAG_NAME} \
-X github.com/tinyauthapp/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/tinyauthapp/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" \
-X github.com/steveiliop56/tinyauth/internal/config.Version=${TAG_NAME} \
-X github.com/steveiliop56/tinyauth/internal/config.CommitHash=${COMMIT_HASH} \
-X github.com/steveiliop56/tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" \
-o ${BIN_NAME} ./cmd/tinyauth
# Build for amd64
+9 -9
View File
@@ -5,13 +5,13 @@
</div>
<div align="center">
<img alt="License" src="https://img.shields.io/github/license/tinyauthapp/tinyauth">
<img alt="Release" src="https://img.shields.io/github/v/release/tinyauthapp/tinyauth">
<img alt="Issues" src="https://img.shields.io/github/issues/tinyauthapp/tinyauth">
<img alt="Tinyauth CI" src="https://github.com/tinyauthapp/tinyauth/actions/workflows/ci.yml/badge.svg">
<img alt="License" src="https://img.shields.io/github/license/steveiliop56/tinyauth">
<img alt="Release" src="https://img.shields.io/github/v/release/steveiliop56/tinyauth">
<img alt="Issues" src="https://img.shields.io/github/issues/steveiliop56/tinyauth">
<img alt="Tinyauth CI" src="https://github.com/steveiliop56/tinyauth/actions/workflows/ci.yml/badge.svg">
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/tinyauth"><img src="https://badges.crowdin.net/tinyauth/localized.svg"></a>
<a href="https://scorecard.dev/viewer/?uri=github.com/tinyauthapp/tinyauth" target="_blank" title="OpenSSF Scorecard">
<img src="https://api.scorecard.dev/projects/github.com/tinyauthapp/tinyauth/badge">
<a href="https://scorecard.dev/viewer/?uri=github.com/steveiliop56/tinyauth" target="_blank" title="OpenSSF Scorecard">
<img src="https://api.scorecard.dev/projects/github.com/steveiliop56/tinyauth/badge">
</a>
</div>
@@ -42,7 +42,7 @@ If you are still not sure if Tinyauth suits your needs you can try out the [demo
You can find documentation and guides on all of the available configuration of Tinyauth in the [website](https://tinyauth.app).
If you wish to contribute to the documentation head over to the [repository](https://github.com/tinyauthapp/docs).
If you wish to contribute to the documentation head over to the [repository](https://github.com/steveiliop56/tinyauth-docs).
## Discord
@@ -50,7 +50,7 @@ Tinyauth has a [Discord](https://discord.gg/eHzVaCzRRd) server. Feel free to hop
## Contributing
All contributions to the codebase are welcome! If you have any free time, feel free to pick up an [issue](https://github.com/tinyauthapp/tinyauth/issues) or add your own missing features. Make sure to check out the [contributing guide](./CONTRIBUTING.md) for instructions on how to get the development server up and running.
All contributions to the codebase are welcome! If you have any free time, feel free to pick up an [issue](https://github.com/steveiliop56/tinyauth/issues) or add your own missing features. Make sure to check out the [contributing guide](./CONTRIBUTING.md) for instructions on how to get the development server up and running.
## Localization
@@ -75,4 +75,4 @@ A big thank you to the following people for providing me with more coffee:
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=tinyauthapp/tinyauth&type=Date)](https://www.star-history.com/#tinyauthapp/tinyauth&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=steveiliop56/tinyauth&type=Date)](https://www.star-history.com/#steveiliop56/tinyauth&Date)
+2 -2
View File
@@ -2,8 +2,8 @@
## Supported Versions
It is recommended to use the [latest](https://github.com/tinyauthapp/tinyauth/releases/latest) available version of tinyauth. This is because it includes security fixes, new features and dependency updates. Older versions, especially major ones, are not supported and won't receive security or patch updates.
It is recommended to use the [latest](https://github.com/steveiliop56/tinyauth/releases/latest) available version of tinyauth. This is because it includes security fixes, new features and dependency updates. Older versions, especially major ones, are not supported and won't receive security or patch updates.
## Reporting a Vulnerability
Due to the nature of this app, it needs to be secure. If you discover any security issues or vulnerabilities in the app please contact me as soon as possible at <security@tinyauth.app>. Please do not use the issues section to report security issues as I won't be able to patch them in time and they may get exploited by malicious actors.
Due to the nature of this app, it needs to be secure. If you discover any security issues or vulnerabilities in the app please contact me as soon as possible at <steve@doesmycode.work>. Please do not use the issues section to report security issues as I won't be able to patch them in time and they may get exploited by malicious actors.
+3 -3
View File
@@ -3,7 +3,7 @@
"embeds": [
{
"title": "Welcome to Tinyauth Discord!",
"description": "Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github and any provider to all of your docker apps. It supports all the popular proxies like Traefik, Nginx and Caddy.\n\n**Information**\n\n• Github: <https://github.com/tinyauthapp/tinyauth>\n• Website: <https://tinyauth.app>",
"description": "Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github and any provider to all of your docker apps. It supports all the popular proxies like Traefik, Nginx and Caddy.\n\n**Information**\n\n• Github: <https://github.com/steveiliop56/tinyauth>\n• Website: <https://tinyauth.app>",
"url": "https://tinyauth.app",
"color": 7002085,
"author": {
@@ -14,9 +14,9 @@
},
"timestamp": "2025-06-06T12:25:27.629Z",
"thumbnail": {
"url": "https://github.com/tinyauthapp/tinyauth/blob/main/assets/logo.png?raw=true"
"url": "https://github.com/steveiliop56/tinyauth/blob/main/assets/logo.png?raw=true"
}
}
],
"attachments": []
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/google/uuid"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/tinyauthapp/paerser/cli"
)
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"strings"
"charm.land/huh/v2"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/tinyauthapp/paerser/cli"
"golang.org/x/crypto/bcrypt"
)
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"os"
"strings"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"charm.land/huh/v2"
"github.com/mdp/qrterminal/v3"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"os"
"time"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/tinyauthapp/paerser/cli"
)
+4 -4
View File
@@ -4,10 +4,10 @@ import (
"fmt"
"charm.land/huh/v2"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/loaders"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/bootstrap"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/loaders"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/rs/zerolog/log"
"github.com/tinyauthapp/paerser/cli"
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"errors"
"fmt"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"charm.land/huh/v2"
"github.com/pquerna/otp/totp"
+1 -1
View File
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/tinyauthapp/paerser/cli"
)
+1 -1
View File
@@ -15,7 +15,7 @@ services:
traefik.http.routers.whoami.middlewares: tinyauth
tinyauth:
image: ghcr.io/tinyauthapp/tinyauth:v5
image: ghcr.io/steveiliop56/tinyauth:v5
environment:
- TINYAUTH_APPURL=https://tinyauth.example.com
- TINYAUTH_AUTH_USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u # user:password
+1 -5
View File
@@ -80,9 +80,5 @@
"profileScopeDescription": "Allows the app to access your profile information.",
"groupsScopeName": "Groups",
"groupsScopeDescription": "Allows the app to access your group information.",
"backToLoginButton": "Back to login",
"phoneScopeName": "Phone",
"phoneScopeDescription": "Allows the app to access your phone number.",
"addressScopeName": "Address",
"addressScopeDescription": "Allows the app to access your address."
"backToLoginButton": "Back to login"
}
+1 -13
View File
@@ -17,7 +17,7 @@ import { toast } from "sonner";
import { useOIDCParams } from "@/lib/hooks/oidc";
import { useTranslation } from "react-i18next";
import { TFunction } from "i18next";
import { Mail, MapPin, Phone, Shield, User, Users } from "lucide-react";
import { Mail, Shield, User, Users } from "lucide-react";
import {
Tooltip,
TooltipContent,
@@ -61,18 +61,6 @@ const createScopeMap = (t: TFunction<"translation", undefined>): Scope[] => {
description: t("groupsScopeDescription"),
icon: <Users {...scopeMapIconProps} />,
},
{
id: "phone",
name: t("phoneScopeName"),
description: t("phoneScopeDescription"),
icon: <Phone {...scopeMapIconProps} />,
},
{
id: "address",
name: t("addressScopeName"),
description: t("addressScopeDescription"),
icon: <MapPin {...scopeMapIconProps} />,
},
];
};
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"reflect"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
)
type EnvEntry struct {
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"reflect"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
)
type MarkdownEntry struct {
+5 -5
View File
@@ -1,4 +1,4 @@
module github.com/tinyauthapp/tinyauth
module github.com/steveiliop56/tinyauth
go 1.26.0
@@ -14,7 +14,7 @@ require (
github.com/google/uuid v1.6.0
github.com/mdp/qrterminal/v3 v3.2.1
github.com/pquerna/otp v1.5.0
github.com/rs/zerolog v1.35.1
github.com/rs/zerolog v1.35.0
github.com/stretchr/testify v1.11.1
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
github.com/weppos/publicsuffix-go v0.50.3
@@ -22,7 +22,7 @@ require (
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
golang.org/x/oauth2 v0.36.0
gotest.tools/v3 v3.5.2
modernc.org/sqlite v1.49.1
modernc.org/sqlite v1.48.2
)
require (
@@ -30,7 +30,7 @@ require (
charm.land/bubbletea/v2 v2.0.2 // indirect
charm.land/lipgloss/v2 v2.0.1 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Azure/go-ntlmssp v0.1.1 // indirect
github.com/Azure/go-ntlmssp v0.1.0 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
@@ -124,7 +124,7 @@ require (
golang.org/x/text v0.36.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.72.0 // indirect
modernc.org/libc v1.70.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
rsc.io/qr v0.2.0 // indirect
+12 -12
View File
@@ -10,8 +10,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw=
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
github.com/Azure/go-ntlmssp v0.1.0/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/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -234,8 +234,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=
github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@@ -329,10 +329,10 @@ 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=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
@@ -341,8 +341,8 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -351,8 +351,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U=
modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
modernc.org/sqlite v1.48.2 h1:5CnW4uP8joZtA0LedVqLbZV5GD7F/0x91AXeSyjoh5c=
modernc.org/sqlite v1.48.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
@@ -1,13 +0,0 @@
ALTER TABLE "oidc_userinfo" DROP COLUMN "given_name";
ALTER TABLE "oidc_userinfo" DROP COLUMN "family_name";
ALTER TABLE "oidc_userinfo" DROP COLUMN "middle_name";
ALTER TABLE "oidc_userinfo" DROP COLUMN "nickname";
ALTER TABLE "oidc_userinfo" DROP COLUMN "profile";
ALTER TABLE "oidc_userinfo" DROP COLUMN "picture";
ALTER TABLE "oidc_userinfo" DROP COLUMN "website";
ALTER TABLE "oidc_userinfo" DROP COLUMN "gender";
ALTER TABLE "oidc_userinfo" DROP COLUMN "birthdate";
ALTER TABLE "oidc_userinfo" DROP COLUMN "zoneinfo";
ALTER TABLE "oidc_userinfo" DROP COLUMN "locale";
ALTER TABLE "oidc_userinfo" DROP COLUMN "phone_number";
ALTER TABLE "oidc_userinfo" DROP COLUMN "address";
@@ -1,13 +0,0 @@
ALTER TABLE "oidc_userinfo" ADD COLUMN "given_name" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "family_name" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "middle_name" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "nickname" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "profile" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "picture" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "website" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "gender" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "birthdate" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "zoneinfo" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "locale" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "phone_number" TEXT NOT NULL DEFAULT "";
ALTER TABLE "oidc_userinfo" ADD COLUMN "address" TEXT NOT NULL DEFAULT "{}";
+8 -6
View File
@@ -12,11 +12,11 @@ import (
"strings"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type BootstrapApp struct {
@@ -44,6 +44,8 @@ func NewBootstrapApp(config config.Config) *BootstrapApp {
}
func (app *BootstrapApp) Setup() error {
fmt.Println("Tinyauth is moving to an organization! All versions after v5.0.7 will be released under ghcr.io/tinyauthapp/tinyauth. Existing images will continue to work but new features and updates (including security ones) will only be released under the new image path.")
// get app url
if app.config.AppURL == "" {
return fmt.Errorf("app URL cannot be empty, perhaps config loading failed")
@@ -63,7 +65,7 @@ func (app *BootstrapApp) Setup() error {
}
// Parse users
users, err := utils.GetUsers(app.config.Auth.Users, app.config.Auth.UsersFile, app.config.Auth.UserAttributes)
users, err := utils.GetUsers(app.config.Auth.Users, app.config.Auth.UsersFile)
if err != nil {
return err
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"github.com/tinyauthapp/tinyauth/internal/assets"
"github.com/steveiliop56/tinyauth/internal/assets"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/sqlite3"
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"fmt"
"slices"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/middleware"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/middleware"
"github.com/gin-gonic/gin"
)
+3 -3
View File
@@ -1,9 +1,9 @@
package bootstrap
import (
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type Services struct {
+9 -39
View File
@@ -113,43 +113,15 @@ type ServerConfig struct {
}
type AuthConfig struct {
IP IPConfig `description:"IP whitelisting config options." yaml:"ip"`
Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
UserAttributes map[string]UserAttributes `description:"Map of per-user OIDC attributes (username -> attributes)." yaml:"userAttributes"`
UsersFile string `description:"Path to the users file." yaml:"usersFile"`
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
SessionExpiry int `description:"Session expiry time in seconds." yaml:"sessionExpiry"`
SessionMaxLifetime int `description:"Maximum session lifetime in seconds." yaml:"sessionMaxLifetime"`
LoginTimeout int `description:"Login timeout in seconds." yaml:"loginTimeout"`
LoginMaxRetries int `description:"Maximum login retries." yaml:"loginMaxRetries"`
TrustedProxies []string `description:"Comma-separated list of trusted proxy addresses." yaml:"trustedProxies"`
}
type UserAttributes struct {
Name string `description:"Full name of the user." yaml:"name"`
GivenName string `description:"Given (first) name of the user." yaml:"givenName"`
FamilyName string `description:"Family (last) name of the user." yaml:"familyName"`
MiddleName string `description:"Middle name of the user." yaml:"middleName"`
Nickname string `description:"Nickname of the user." yaml:"nickname"`
Profile string `description:"URL of the user's profile page." yaml:"profile"`
Picture string `description:"URL of the user's profile picture." yaml:"picture"`
Website string `description:"URL of the user's website." yaml:"website"`
Email string `description:"Email address of the user." yaml:"email"`
Gender string `description:"Gender of the user." yaml:"gender"`
Birthdate string `description:"Birthdate of the user (YYYY-MM-DD)." yaml:"birthdate"`
Zoneinfo string `description:"Time zone of the user (e.g. Europe/Athens)." yaml:"zoneinfo"`
Locale string `description:"Locale of the user (e.g. en-US)." yaml:"locale"`
PhoneNumber string `description:"Phone number of the user." yaml:"phoneNumber"`
Address AddressClaim `description:"Address of the user." yaml:"address"`
}
type AddressClaim struct {
Formatted string `description:"Full mailing address, formatted for display." yaml:"formatted" json:"formatted,omitempty"`
StreetAddress string `description:"Street address." yaml:"streetAddress" json:"street_address,omitempty"`
Locality string `description:"City or locality." yaml:"locality" json:"locality,omitempty"`
Region string `description:"State, province, or region." yaml:"region" json:"region,omitempty"`
PostalCode string `description:"Zip or postal code." yaml:"postalCode" json:"postal_code,omitempty"`
Country string `description:"Country." yaml:"country" json:"country,omitempty"`
IP IPConfig `description:"IP whitelisting config options." yaml:"ip"`
Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
UsersFile string `description:"Path to the users file." yaml:"usersFile"`
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
SessionExpiry int `description:"Session expiry time in seconds." yaml:"sessionExpiry"`
SessionMaxLifetime int `description:"Maximum session lifetime in seconds." yaml:"sessionMaxLifetime"`
LoginTimeout int `description:"Login timeout in seconds." yaml:"loginTimeout"`
LoginMaxRetries int `description:"Maximum login retries." yaml:"loginMaxRetries"`
TrustedProxies []string `description:"Comma-separated list of trusted proxy addresses." yaml:"trustedProxies"`
}
type IPConfig struct {
@@ -256,7 +228,6 @@ type User struct {
Username string
Password string
TotpSecret string
Attributes UserAttributes
}
type LdapUser struct {
@@ -283,7 +254,6 @@ type UserContext struct {
OAuthName string
OAuthSub string
LdapGroups string
Attributes UserAttributes
}
// API responses and queries
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"fmt"
"net/url"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
)
@@ -7,10 +7,10 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
)
@@ -7,8 +7,8 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
)
+5 -5
View File
@@ -6,11 +6,11 @@ import (
"strings"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
"github.com/google/go-querystring/query"
+3 -3
View File
@@ -10,9 +10,9 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/go-querystring/query"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type OIDCControllerConfig struct{}
+6 -6
View File
@@ -12,12 +12,12 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/go-querystring/query"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/bootstrap"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+4 -4
View File
@@ -8,10 +8,10 @@ import (
"regexp"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
"github.com/google/go-querystring/query"
+6 -6
View File
@@ -6,12 +6,12 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/bootstrap"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -7,8 +7,8 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+6 -39
View File
@@ -4,11 +4,10 @@ import (
"fmt"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
"github.com/pquerna/otp/totp"
@@ -106,32 +105,16 @@ func (controller *UserController) loginHandler(c *gin.Context) {
controller.auth.RecordLoginAttempt(req.Username, true)
var localUser *config.User
if userSearch.Type == "local" {
user := controller.auth.GetLocalUser(userSearch.Username)
localUser = &user
}
if userSearch.Type == "local" && localUser != nil {
user := *localUser
if user.TotpSecret != "" {
tlog.App.Debug().Str("username", req.Username).Msg("User has TOTP enabled, requiring TOTP verification")
name := user.Attributes.Name
if name == "" {
name = utils.Capitalize(user.Username)
}
email := user.Attributes.Email
if email == "" {
email = utils.CompileUserEmail(user.Username, controller.config.CookieDomain)
}
err := controller.auth.CreateSessionCookie(c, &repository.Session{
Username: user.Username,
Name: name,
Email: email,
Name: utils.Capitalize(user.Username),
Email: utils.CompileUserEmail(user.Username, controller.config.CookieDomain),
Provider: "local",
TotpPending: true,
})
@@ -161,15 +144,6 @@ func (controller *UserController) loginHandler(c *gin.Context) {
Provider: "local",
}
if userSearch.Type == "local" && localUser != nil {
if localUser.Attributes.Name != "" {
sessionCookie.Name = localUser.Attributes.Name
}
if localUser.Attributes.Email != "" {
sessionCookie.Email = localUser.Attributes.Email
}
}
if userSearch.Type == "ldap" {
sessionCookie.Provider = "ldap"
}
@@ -284,13 +258,6 @@ func (controller *UserController) totpHandler(c *gin.Context) {
Provider: "local",
}
if user.Attributes.Name != "" {
sessionCookie.Name = user.Attributes.Name
}
if user.Attributes.Email != "" {
sessionCookie.Email = user.Attributes.Email
}
tlog.App.Trace().Interface("session_cookie", sessionCookie).Msg("Creating session cookie")
err = controller.auth.CreateSessionCookie(c, &sessionCookie)
+21 -109
View File
@@ -4,18 +4,19 @@ import (
"encoding/json"
"net/http/httptest"
"path"
"slices"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/pquerna/otp/totp"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/bootstrap"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -35,23 +36,6 @@ func TestUserController(t *testing.T) {
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
},
{
Username: "attruser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
Attributes: config.UserAttributes{
Name: "Alice Smith",
Email: "alice@example.com",
},
},
{
Username: "attrtotpuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
Attributes: config.UserAttributes{
Name: "Bob Jones",
Email: "bob@example.com",
},
},
},
SessionExpiry: 10, // 10 seconds, useful for testing
CookieDomain: "example.com",
@@ -289,64 +273,6 @@ func TestUserController(t *testing.T) {
assert.Contains(t, recorder.Body.String(), "Too many failed TOTP attempts.")
},
},
{
description: "Login uses name and email from user attributes",
middlewares: []gin.HandlerFunc{},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
loginReq := controller.LoginRequest{Username: "attruser", Password: "password"}
body, err := json.Marshal(loginReq)
require.NoError(t, err)
req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(body)))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(recorder, req)
require.Equal(t, 200, recorder.Code)
cookies := recorder.Result().Cookies()
require.Len(t, cookies, 1)
assert.Equal(t, "tinyauth-session", cookies[0].Name)
},
},
{
description: "Login with TOTP uses name and email from user attributes in pending session",
middlewares: []gin.HandlerFunc{},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
loginReq := controller.LoginRequest{Username: "attrtotpuser", Password: "password"}
body, err := json.Marshal(loginReq)
require.NoError(t, err)
req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(body)))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(recorder, req)
require.Equal(t, 200, recorder.Code)
var res map[string]any
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &res))
assert.Equal(t, true, res["totpPending"])
require.Len(t, recorder.Result().Cookies(), 1)
},
},
{
description: "TOTP completion uses name and email from user attributes",
middlewares: []gin.HandlerFunc{},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
code, err := totp.GenerateCode("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK", time.Now())
require.NoError(t, err)
totpReq := controller.TotpRequest{Code: code}
body, err := json.Marshal(totpReq)
require.NoError(t, err)
req := httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(body)))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(recorder, req)
require.Equal(t, 200, recorder.Code)
cookies := recorder.Result().Cookies()
require.Len(t, cookies, 1)
assert.Equal(t, "tinyauth-session", cookies[0].Name)
},
},
}
oauthBrokerCfgs := make(map[string]config.OAuthServiceConfig)
@@ -379,31 +305,9 @@ func TestUserController(t *testing.T) {
authService.ClearRateLimitsTestingOnly()
}
setTotpMiddlewareOverrides := map[string]config.UserContext{
"Should be able to login with totp": {
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
"Totp should rate limit on multiple invalid attempts": {
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
"TOTP completion uses name and email from user attributes": {
Username: "attrtotpuser",
Name: "Bob Jones",
Email: "bob@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
setTotpMiddlewareOverrides := []string{
"Should be able to login with totp",
"Totp should rate limit on multiple invalid attempts",
}
for _, test := range tests {
@@ -417,10 +321,18 @@ func TestUserController(t *testing.T) {
// Gin is stupid and doesn't allow setting a middleware after the groups
// so we need to do some stupid overrides here
if ctx, ok := setTotpMiddlewareOverrides[test.description]; ok {
ctx := ctx
if slices.Contains(setTotpMiddlewareOverrides, test.description) {
// Assuming the cookie is set, it should be picked up by the
// context middleware
router.Use(func(c *gin.Context) {
c.Set("context", &ctx)
c.Set("context", &config.UserContext{
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
})
})
}
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/service"
)
type OpenIDConnectConfiguration struct {
@@ -61,7 +61,7 @@ func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context
SubjectTypesSupported: []string{"pairwise"},
IDTokenSigningAlgValuesSupported: []string{"RS256"},
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups", "phone_number", "phone_number_verified", "address", "given_name", "family_name", "middle_name", "nickname", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale"},
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups"},
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"none"},
@@ -8,12 +8,12 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/bootstrap"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -67,7 +67,7 @@ func TestWellKnownController(t *testing.T) {
SubjectTypesSupported: []string{"pairwise"},
IDTokenSigningAlgValuesSupported: []string{"RS256"},
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups", "phone_number", "phone_number_verified", "address", "given_name", "family_name", "middle_name", "nickname", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale"},
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups"},
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"none"},
+6 -23
View File
@@ -4,10 +4,10 @@ import (
"strings"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/service"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
)
@@ -99,7 +99,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
}
var ldapGroups []string
var localAttributes config.UserAttributes
if cookie.Provider == "ldap" {
ldapUser, err := m.auth.GetLdapUser(userSearch.Username)
@@ -113,11 +112,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
ldapGroups = ldapUser.Groups
}
if cookie.Provider == "local" {
localUser := m.auth.GetLocalUser(cookie.Username)
localAttributes = localUser.Attributes
}
m.auth.RefreshSessionCookie(c)
c.Set("context", &config.UserContext{
Username: cookie.Username,
@@ -126,7 +120,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
Provider: cookie.Provider,
IsLoggedIn: true,
LdapGroups: strings.Join(ldapGroups, ","),
Attributes: localAttributes,
})
c.Next()
return
@@ -209,23 +202,13 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
return
}
name := utils.Capitalize(user.Username)
if user.Attributes.Name != "" {
name = user.Attributes.Name
}
email := utils.CompileUserEmail(user.Username, m.config.CookieDomain)
if user.Attributes.Email != "" {
email = user.Attributes.Email
}
c.Set("context", &config.UserContext{
Username: user.Username,
Name: name,
Email: email,
Name: utils.Capitalize(user.Username),
Email: utils.CompileUserEmail(user.Username, m.config.CookieDomain),
Provider: "local",
IsLoggedIn: true,
IsBasicAuth: true,
Attributes: user.Attributes,
})
c.Next()
return
+2 -2
View File
@@ -8,8 +8,8 @@ import (
"strings"
"time"
"github.com/tinyauthapp/tinyauth/internal/assets"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/assets"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
)
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
// See context middleware for explanation of why we have to do this
-13
View File
@@ -34,19 +34,6 @@ type OidcUserinfo struct {
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
}
type Session struct {
+4 -69
View File
@@ -124,24 +124,11 @@ INSERT INTO "oidc_userinfo" (
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
"updated_at"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
?, ?, ?, ?, ?, ?
)
RETURNING sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address
RETURNING sub, name, preferred_username, email, "groups", updated_at
`
type CreateOidcUserInfoParams struct {
@@ -151,19 +138,6 @@ type CreateOidcUserInfoParams struct {
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
}
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
@@ -174,19 +148,6 @@ func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfo
arg.Email,
arg.Groups,
arg.UpdatedAt,
arg.GivenName,
arg.FamilyName,
arg.MiddleName,
arg.Nickname,
arg.Profile,
arg.Picture,
arg.Website,
arg.Gender,
arg.Birthdate,
arg.Zoneinfo,
arg.Locale,
arg.PhoneNumber,
arg.Address,
)
var i OidcUserinfo
err := row.Scan(
@@ -196,19 +157,6 @@ func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfo
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
@@ -508,7 +456,7 @@ func (q *Queries) GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken,
}
const getOidcUserInfo = `-- name: GetOidcUserInfo :one
SELECT sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address FROM "oidc_userinfo"
SELECT sub, name, preferred_username, email, "groups", updated_at FROM "oidc_userinfo"
WHERE "sub" = ?
`
@@ -522,19 +470,6 @@ func (q *Queries) GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"errors"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type AccessControlsService struct {
+4 -4
View File
@@ -10,10 +10,10 @@ import (
"sync"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"context"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/decoders"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
container "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/cenkalti/backoff/v5"
ldapgo "github.com/go-ldap/ldap/v3"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type LdapServiceConfig struct {
+2 -2
View File
@@ -1,8 +1,8 @@
package service
import (
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"golang.org/x/exp/slices"
"golang.org/x/oauth2"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"net/http"
"strconv"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
)
type GithubEmailResponse []struct {
+1 -1
View File
@@ -1,7 +1,7 @@
package service
import (
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
"golang.org/x/oauth2/endpoints"
)
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"net/http"
"time"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
"golang.org/x/oauth2"
)
+15 -81
View File
@@ -20,15 +20,15 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-jose/go-jose/v4"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"golang.org/x/exp/slices"
)
var (
SupportedScopes = []string{"openid", "profile", "email", "phone", "address", "groups"}
SupportedScopes = []string{"openid", "profile", "email", "groups"}
SupportedResponseTypes = []string{"code"}
SupportedGrantTypes = []string{"authorization_code", "refresh_token"}
)
@@ -48,17 +48,6 @@ type ClaimSet struct {
Iat int64 `json:"iat"`
Exp int64 `json:"exp"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
Nickname string `json:"nickname,omitempty"`
Profile string `json:"profile,omitempty"`
Picture string `json:"picture,omitempty"`
Website string `json:"website,omitempty"`
Gender string `json:"gender,omitempty"`
Birthdate string `json:"birthdate,omitempty"`
Zoneinfo string `json:"zoneinfo,omitempty"`
Locale string `json:"locale,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
@@ -67,27 +56,13 @@ type ClaimSet struct {
}
type UserinfoResponse struct {
Sub string `json:"sub"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
Nickname string `json:"nickname,omitempty"`
Profile string `json:"profile,omitempty"`
Picture string `json:"picture,omitempty"`
Website string `json:"website,omitempty"`
Gender string `json:"gender,omitempty"`
Birthdate string `json:"birthdate,omitempty"`
Zoneinfo string `json:"zoneinfo,omitempty"`
Locale string `json:"locale,omitempty"`
Email string `json:"email,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Groups []string `json:"groups,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
PhoneNumberVerified *bool `json:"phone_number_verified,omitempty"`
Address *config.AddressClaim `json:"address,omitempty"`
UpdatedAt int64 `json:"updated_at"`
Sub string `json:"sub"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Groups []string `json:"groups,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
UpdatedAt int64 `json:"updated_at"`
}
type TokenResponse struct {
@@ -367,30 +342,12 @@ func (service *OIDCService) StoreCode(c *gin.Context, sub string, code string, r
}
func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContext config.UserContext, req AuthorizeRequest) error {
addressJSON, err := json.Marshal(userContext.Attributes.Address)
if err != nil {
return err
}
userInfoParams := repository.CreateOidcUserInfoParams{
Sub: sub,
Name: userContext.Name,
Email: userContext.Email,
PreferredUsername: userContext.Username,
UpdatedAt: time.Now().Unix(),
GivenName: userContext.Attributes.GivenName,
FamilyName: userContext.Attributes.FamilyName,
MiddleName: userContext.Attributes.MiddleName,
Nickname: userContext.Attributes.Nickname,
Profile: userContext.Attributes.Profile,
Picture: userContext.Attributes.Picture,
Website: userContext.Attributes.Website,
Gender: userContext.Attributes.Gender,
Birthdate: userContext.Attributes.Birthdate,
Zoneinfo: userContext.Attributes.Zoneinfo,
Locale: userContext.Attributes.Locale,
PhoneNumber: userContext.Attributes.PhoneNumber,
Address: string(addressJSON),
}
// Tinyauth will pass through the groups it got from an LDAP or an OIDC server
@@ -402,7 +359,7 @@ func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContex
userInfoParams.Groups = userContext.OAuthGroups
}
_, err = service.queries.CreateOidcUserInfo(c, userInfoParams)
_, err := service.queries.CreateOidcUserInfo(c, userInfoParams)
return err
}
@@ -680,22 +637,12 @@ func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope
if slices.Contains(scopes, "profile") {
userInfo.Name = user.Name
userInfo.PreferredUsername = user.PreferredUsername
userInfo.GivenName = user.GivenName
userInfo.FamilyName = user.FamilyName
userInfo.MiddleName = user.MiddleName
userInfo.Nickname = user.Nickname
userInfo.Profile = user.Profile
userInfo.Picture = user.Picture
userInfo.Website = user.Website
userInfo.Gender = user.Gender
userInfo.Birthdate = user.Birthdate
userInfo.Zoneinfo = user.Zoneinfo
userInfo.Locale = user.Locale
}
if slices.Contains(scopes, "email") {
userInfo.Email = user.Email
userInfo.EmailVerified = user.Email != ""
// We can set this as a configuration option in the future but for now it's a good idea to assume it's true
userInfo.EmailVerified = true
}
if slices.Contains(scopes, "groups") {
@@ -706,19 +653,6 @@ func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope
}
}
if slices.Contains(scopes, "phone") {
userInfo.PhoneNumber = user.PhoneNumber
verified := user.PhoneNumber != ""
userInfo.PhoneNumberVerified = &verified
}
if slices.Contains(scopes, "address") {
var addr config.AddressClaim
if err := json.Unmarshal([]byte(user.Address), &addr); err == nil {
userInfo.Address = &addr
}
}
return userInfo
}
-198
View File
@@ -1,198 +0,0 @@
package service_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
)
func newTestUser() repository.OidcUserinfo {
addr := config.AddressClaim{
Formatted: "123 Main St",
StreetAddress: "123 Main St",
Locality: "Springfield",
Region: "IL",
PostalCode: "62701",
Country: "US",
}
addrJSON, _ := json.Marshal(addr)
return repository.OidcUserinfo{
Sub: "test-sub",
Name: "Test User",
PreferredUsername: "testuser",
Email: "test@example.com",
Groups: "admins,users",
UpdatedAt: 1234567890,
GivenName: "Test",
FamilyName: "User",
MiddleName: "M",
Nickname: "testy",
Profile: "https://example.com/testuser",
Picture: "https://example.com/testuser.jpg",
Website: "https://testuser.example.com",
Gender: "male",
Birthdate: "1990-01-01",
Zoneinfo: "America/Chicago",
Locale: "en-US",
PhoneNumber: "+15555550100",
Address: string(addrJSON),
}
}
func TestCompileUserinfo(t *testing.T) {
dir := t.TempDir()
svc := service.NewOIDCService(service.OIDCServiceConfig{
PrivateKeyPath: dir + "/key.pem",
PublicKeyPath: dir + "/key.pub",
Issuer: "https://tinyauth.example.com",
SessionExpiry: 3600,
}, nil)
require.NoError(t, svc.Init())
type testCase struct {
description string
mutate func(u *repository.OidcUserinfo)
scope string
run func(t *testing.T, info service.UserinfoResponse)
}
tests := []testCase{
{
description: "openid scope only returns sub and updated_at",
scope: "openid",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, "test-sub", info.Sub)
assert.Equal(t, int64(1234567890), info.UpdatedAt)
assert.Empty(t, info.Name)
assert.Empty(t, info.Email)
assert.Nil(t, info.Groups)
assert.Nil(t, info.PhoneNumberVerified)
assert.Nil(t, info.Address)
},
},
{
description: "profile scope returns all profile fields",
scope: "openid,profile",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, "Test User", info.Name)
assert.Equal(t, "testuser", info.PreferredUsername)
assert.Equal(t, "Test", info.GivenName)
assert.Equal(t, "User", info.FamilyName)
assert.Equal(t, "M", info.MiddleName)
assert.Equal(t, "testy", info.Nickname)
assert.Equal(t, "https://example.com/testuser", info.Profile)
assert.Equal(t, "https://example.com/testuser.jpg", info.Picture)
assert.Equal(t, "https://testuser.example.com", info.Website)
assert.Equal(t, "male", info.Gender)
assert.Equal(t, "1990-01-01", info.Birthdate)
assert.Equal(t, "America/Chicago", info.Zoneinfo)
assert.Equal(t, "en-US", info.Locale)
assert.Empty(t, info.Email)
},
},
{
description: "email scope sets email and email_verified true when email present",
scope: "openid,email",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, "test@example.com", info.Email)
assert.True(t, info.EmailVerified)
assert.Empty(t, info.Name)
},
},
{
description: "email scope sets email_verified false when email absent",
scope: "openid,email",
mutate: func(u *repository.OidcUserinfo) { u.Email = "" },
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Empty(t, info.Email)
assert.False(t, info.EmailVerified)
},
},
{
description: "phone scope sets phone_number_verified true when phone present",
scope: "openid,phone",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, "+15555550100", info.PhoneNumber)
require.NotNil(t, info.PhoneNumberVerified)
assert.True(t, *info.PhoneNumberVerified)
},
},
{
description: "phone scope sets phone_number_verified false when phone absent",
scope: "openid,phone",
mutate: func(u *repository.OidcUserinfo) { u.PhoneNumber = "" },
run: func(t *testing.T, info service.UserinfoResponse) {
require.NotNil(t, info.PhoneNumberVerified)
assert.False(t, *info.PhoneNumberVerified)
},
},
{
description: "address scope returns parsed address",
scope: "openid,address",
run: func(t *testing.T, info service.UserinfoResponse) {
require.NotNil(t, info.Address)
assert.Equal(t, "123 Main St", info.Address.Formatted)
assert.Equal(t, "123 Main St", info.Address.StreetAddress)
assert.Equal(t, "Springfield", info.Address.Locality)
assert.Equal(t, "IL", info.Address.Region)
assert.Equal(t, "62701", info.Address.PostalCode)
assert.Equal(t, "US", info.Address.Country)
},
},
{
description: "address scope with invalid JSON omits address",
scope: "openid,address",
mutate: func(u *repository.OidcUserinfo) { u.Address = "not-valid-json" },
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Nil(t, info.Address)
},
},
{
description: "groups scope returns split groups",
scope: "openid,groups",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, []string{"admins", "users"}, info.Groups)
},
},
{
description: "groups scope returns empty slice when no groups",
scope: "openid,groups",
mutate: func(u *repository.OidcUserinfo) { u.Groups = "" },
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, []string{}, info.Groups)
},
},
{
description: "all scopes return all fields",
scope: "openid,profile,email,phone,address,groups",
run: func(t *testing.T, info service.UserinfoResponse) {
assert.Equal(t, "Test User", info.Name)
assert.Equal(t, "test@example.com", info.Email)
assert.Equal(t, "+15555550100", info.PhoneNumber)
require.NotNil(t, info.PhoneNumberVerified)
assert.True(t, *info.PhoneNumberVerified)
require.NotNil(t, info.Address)
assert.Equal(t, "Springfield", info.Address.Locality)
assert.Equal(t, []string{"admins", "users"}, info.Groups)
},
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
user := newTestUser()
if test.mutate != nil {
test.mutate(&user)
}
info := svc.CompileUserinfo(user, test.scope)
test.run(t, info)
})
}
}
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"net/url"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/gin-gonic/gin"
"github.com/weppos/publicsuffix-go/publicsuffix"
+2 -2
View File
@@ -3,8 +3,8 @@ package utils_test
import (
"testing"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/gin-gonic/gin"
"gotest.tools/v3/assert"
@@ -3,8 +3,8 @@ package decoders_test
import (
"testing"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/decoders"
"gotest.tools/v3/assert"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package utils_test
import (
"testing"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils"
"gotest.tools/v3/assert"
)
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"os"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/tinyauthapp/paerser/cli"
"github.com/tinyauthapp/paerser/env"
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"os"
"testing"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils"
"gotest.tools/v3/assert"
)
+1 -1
View File
@@ -3,7 +3,7 @@ package utils_test
import (
"testing"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils"
"gotest.tools/v3/assert"
)
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
)
type Logger struct {
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"encoding/json"
"testing"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/steveiliop56/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
"github.com/rs/zerolog"
"gotest.tools/v3/assert"
+4 -7
View File
@@ -6,10 +6,10 @@ import (
"net/mail"
"strings"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/steveiliop56/tinyauth/internal/config"
)
func ParseUsers(usersStr []string, userAttributes map[string]config.UserAttributes) ([]config.User, error) {
func ParseUsers(usersStr []string) ([]config.User, error) {
var users []config.User
if len(usersStr) == 0 {
@@ -24,16 +24,13 @@ func ParseUsers(usersStr []string, userAttributes map[string]config.UserAttribut
if err != nil {
return []config.User{}, err
}
if attrs, ok := userAttributes[parsed.Username]; ok {
parsed.Attributes = attrs
}
users = append(users, parsed)
}
return users, nil
}
func GetUsers(usersCfg []string, usersPath string, userAttributes map[string]config.UserAttributes) ([]config.User, error) {
func GetUsers(usersCfg []string, usersPath string) ([]config.User, error) {
var usersStr []string
if len(usersCfg) == 0 && usersPath == "" {
@@ -62,7 +59,7 @@ func GetUsers(usersCfg []string, usersPath string, userAttributes map[string]con
}
}
return ParseUsers(usersStr, userAttributes)
return ParseUsers(usersStr)
}
func ParseUser(userStr string) (config.User, error) {
+53 -48
View File
@@ -4,117 +4,122 @@ import (
"os"
"testing"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/steveiliop56/tinyauth/internal/utils"
"gotest.tools/v3/assert"
)
func TestGetUsers(t *testing.T) {
hash := "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"
// Setup
file, err := os.Create("/tmp/tinyauth_users_test.txt")
assert.NilError(t, err)
_, err = file.WriteString(" user1:" + hash + " \n user2:" + hash + " ") // Spacing is on purpose
_, err = file.WriteString(" user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G \n user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G ") // Spacing is on purpose
assert.NilError(t, err)
err = file.Close()
assert.NilError(t, err)
defer os.Remove("/tmp/tinyauth_users_test.txt")
noAttrs := map[string]config.UserAttributes{}
// Test file only
users, err := utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt", noAttrs)
// Test file
users, err := utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt")
assert.NilError(t, err)
assert.Equal(t, 2, len(users))
assert.Equal(t, "user1", users[0].Username)
assert.Equal(t, hash, users[0].Password)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
assert.Equal(t, "user2", users[1].Username)
assert.Equal(t, hash, users[1].Password)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
// Test inline config only
users, err = utils.GetUsers([]string{"user3:" + hash, "user4:" + hash}, "", noAttrs)
// Test config
users, err = utils.GetUsers([]string{"user3:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", "user4:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"}, "")
assert.NilError(t, err)
assert.Equal(t, 2, len(users))
assert.Equal(t, "user3", users[0].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
assert.Equal(t, "user4", users[1].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
// Test both
users, err = utils.GetUsers([]string{"user5:" + hash}, "/tmp/tinyauth_users_test.txt", noAttrs)
users, err = utils.GetUsers([]string{"user5:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"}, "/tmp/tinyauth_users_test.txt")
assert.NilError(t, err)
assert.Equal(t, 3, len(users))
usernames := map[string]bool{}
for _, u := range users {
usernames[u.Username] = true
}
assert.Assert(t, usernames["user1"])
assert.Assert(t, usernames["user2"])
assert.Assert(t, usernames["user5"])
// Test attributes applied from userAttributes map
attrs := map[string]config.UserAttributes{
"user1": {Name: "User One", Email: "user1@example.com"},
}
users, err = utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt", attrs)
assert.NilError(t, err)
assert.Equal(t, 2, len(users))
for _, u := range users {
if u.Username == "user1" {
assert.Equal(t, "User One", u.Attributes.Name)
assert.Equal(t, "user1@example.com", u.Attributes.Email)
}
if u.Username == "user2" {
assert.Equal(t, "", u.Attributes.Name)
}
}
assert.Equal(t, "user5", users[0].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
assert.Equal(t, "user1", users[1].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
assert.Equal(t, "user2", users[2].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[2].Password)
// Test empty
users, err = utils.GetUsers([]string{}, "", noAttrs)
users, err = utils.GetUsers([]string{}, "")
assert.NilError(t, err)
assert.Equal(t, 0, len(users))
// Test non-existent file
users, err = utils.GetUsers([]string{}, "/tmp/non_existent_file.txt", noAttrs)
users, err = utils.GetUsers([]string{}, "/tmp/non_existent_file.txt")
assert.ErrorContains(t, err, "no such file or directory")
assert.Equal(t, 0, len(users))
}
func TestParseUser(t *testing.T) {
hash := "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"
func TestParseUsers(t *testing.T) {
// Valid users
users, err := utils.ParseUsers([]string{"user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", "user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF"}) // user2 has TOTP
assert.NilError(t, err)
assert.Equal(t, 2, len(users))
assert.Equal(t, "user1", users[0].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
assert.Equal(t, "", users[0].TotpSecret)
assert.Equal(t, "user2", users[1].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
assert.Equal(t, "ABCDEF", users[1].TotpSecret)
// Valid weirdly spaced users
users, err = utils.ParseUsers([]string{" user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G ", " user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF "}) // Spacing is on purpose
assert.NilError(t, err)
assert.Equal(t, 2, len(users))
assert.Equal(t, "user1", users[0].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
assert.Equal(t, "", users[0].TotpSecret)
assert.Equal(t, "user2", users[1].Username)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
assert.Equal(t, "ABCDEF", users[1].TotpSecret)
}
func TestParseUser(t *testing.T) {
// Valid user without TOTP
user, err := utils.ParseUser("user1:" + hash)
user, err := utils.ParseUser("user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G")
assert.NilError(t, err)
assert.Equal(t, "user1", user.Username)
assert.Equal(t, hash, user.Password)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", user.Password)
assert.Equal(t, "", user.TotpSecret)
// Valid user with TOTP
user, err = utils.ParseUser("user2:" + hash + ":ABCDEF")
user, err = utils.ParseUser("user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF")
assert.NilError(t, err)
assert.Equal(t, "user2", user.Username)
assert.Equal(t, hash, user.Password)
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", user.Password)
assert.Equal(t, "ABCDEF", user.TotpSecret)
// Valid user with $$ in password
+2 -15
View File
@@ -95,22 +95,9 @@ INSERT INTO "oidc_userinfo" (
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
"updated_at"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
?, ?, ?, ?, ?, ?
)
RETURNING *;
+6 -19
View File
@@ -22,23 +22,10 @@ CREATE TABLE IF NOT EXISTS "oidc_tokens" (
);
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" INTEGER NOT NULL,
"given_name" TEXT NOT NULL,
"family_name" TEXT NOT NULL,
"middle_name" TEXT NOT NULL,
"nickname" TEXT NOT NULL,
"profile" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"website" TEXT NOT NULL,
"gender" TEXT NOT NULL,
"birthdate" TEXT NOT NULL,
"zoneinfo" TEXT NOT NULL,
"locale" TEXT NOT NULL,
"phone_number" TEXT NOT NULL,
"address" TEXT NOT NULL
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" INTEGER NOT NULL
);