Compare commits

..
13 Commits
Author SHA1 Message Date
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
653b747829 chore(deps): bump the minor-patch group in /frontend with 6 updates (#1121)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 20:03:52 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ba79a30f2e chore(deps): bump the minor-patch group with 3 updates (#1120)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-07 20:03:34 +03:00
Stavros 8c5094bbcd chore: remove instapods sponsor 2026-09-07 19:59:50 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c28d9fe55e chore(deps): bump the minor-patch group in /frontend with 3 updates (#1117)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-06 20:00:12 +03:00
StavrosandGitHub f58a6dccf5 New Crowdin updates (#1118) 2026-09-06 19:59:49 +03:00
StavrosandOpenAI 0cf64e8598 fix: use constant-time secret comparisons
Co-Authored-By: OpenAI <noreply@openai.com>
2026-09-06 19:56:58 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b61fc6b54e chore(deps): bump the minor-patch group across 1 directory with 3 updates (#1116)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 12:15:04 +03:00
StavrosandGitHub bbcc8cafa6 feat: add backoff to docker connection for proxies (#1114) 2026-09-03 12:14:30 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Stavros
b63a6917fe chore(deps): bump softprops/action-gh-release from 3.0.2 to 3.0.3 (#1111)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stavros <steveiliop56@gmail.com>
2026-09-01 16:08:10 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
59116f7344 chore(deps): bump the minor-patch group in /frontend with 3 updates (#1112)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 16:07:18 +03:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
97eb1efb0e chore(deps): bump node from 26.7-alpine3.23 to 26.8-alpine3.23 (#1106)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 16:06:58 +03:00
Stavros 1112d84667 fix: use retries in e2e tests and increase timeouts 2026-09-01 16:04:04 +03:00
StavrosandGitHub b01f49cc44 refactor: use optimized image for background (#1110) 2026-09-01 00:18:35 +03:00
20 changed files with 649 additions and 501 deletions
+1 -1
View File
@@ -204,7 +204,7 @@ TINYAUTH_UI_TITLE="Tinyauth"
# Message displayed on the forgot password page. # Message displayed on the forgot password page.
TINYAUTH_UI_FORGOTPASSWORDMESSAGE="You can change your password by changing the configuration." TINYAUTH_UI_FORGOTPASSWORDMESSAGE="You can change your password by changing the configuration."
# Path to the background image. # Path to the background image.
TINYAUTH_UI_BACKGROUNDIMAGE="/background.jpg" TINYAUTH_UI_BACKGROUNDIMAGE="/background.webp"
# Enable UI warnings. # Enable UI warnings.
TINYAUTH_UI_WARNINGSENABLED=true TINYAUTH_UI_WARNINGSENABLED=true
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
REPO: ${{ github.event.repository.name }} REPO: ${{ github.event.repository.name }}
- name: Create release - name: Create release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3 uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with: with:
prerelease: true prerelease: true
tag_name: nightly tag_name: nightly
@@ -476,7 +476,7 @@ jobs:
merge-multiple: true merge-multiple: true
- name: Release - name: Release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3 uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with: with:
files: binaries/* files: binaries/*
tag_name: nightly tag_name: nightly
+1 -1
View File
@@ -449,6 +449,6 @@ jobs:
merge-multiple: true merge-multiple: true
- name: Release - name: Release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3 uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with: with:
files: binaries/* files: binaries/*
+1 -1
View File
@@ -1,5 +1,5 @@
# Site builder # Site builder
FROM node:26.7-alpine3.23 AS frontend-builder FROM node:26.8-alpine3.23 AS frontend-builder
WORKDIR /frontend WORKDIR /frontend
+1 -1
View File
@@ -1,5 +1,5 @@
# Site builder # Site builder
FROM node:26.7-alpine3.23 AS frontend-builder FROM node:26.8-alpine3.23 AS frontend-builder
WORKDIR /frontend WORKDIR /frontend
+1 -2
View File
@@ -81,10 +81,9 @@ A huge thank you to [selfh.st](https://selfh.st) for their generous donation to
- [JetBrains for Open-Source](https://jb.gg/OpenSource) - [JetBrains for Open-Source](https://jb.gg/OpenSource)
- [CodeRabbit AI](https://www.coderabbit.ai) - [CodeRabbit AI](https://www.coderabbit.ai)
- [InstaPods](https://instapods.com) - [Deploy Tinyauth from 3$/month](https://app.instapods.com/dashboard/pods/create?app=tinyauth&ref=tinyauth)
## Acknowledgements ## Acknowledgements
- **Freepik** for providing the police hat and badge. - **Freepik** for providing the police hat and badge.
- **Renee French** for the original gopher logo. - **Renee French** for the original gopher logo.
- **Syrhu** for providing the background image of the app. - [Siru Zhou](https://unsplash.com/@syrhu) for providing the background image of the app.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

+5 -1
View File
@@ -4,9 +4,13 @@ export default defineConfig({
testDir: './specs', testDir: './specs',
fullyParallel: true, fullyParallel: true,
forbidOnly: false, forbidOnly: false,
retries: 0, retries: 3,
workers: 4, workers: 4,
reporter: 'html', reporter: 'html',
timeout: 60_000,
expect: {
timeout: 10_000,
},
use: { use: {
trace: 'on-first-retry', trace: 'on-first-retry',
video: 'on', video: 'on',
+11 -11
View File
@@ -23,38 +23,38 @@
"axios": "^1.20.0", "axios": "^1.20.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"i18next": "^26.4.0", "i18next": "^26.4.2",
"i18next-browser-languagedetector": "^8.2.1", "i18next-browser-languagedetector": "^8.2.1",
"i18next-resources-to-backend": "^1.2.3", "i18next-resources-to-backend": "^1.2.3",
"lucide-react": "^1.35.0", "lucide-react": "^1.40.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"radix-ui": "^1.6.7", "radix-ui": "^1.6.7",
"react": "^19.2.8", "react": "^19.2.8",
"react-dom": "^19.2.8", "react-dom": "^19.2.8",
"react-hook-form": "^7.86.0", "react-hook-form": "^7.87.0",
"react-i18next": "^17.0.12", "react-i18next": "^17.0.13",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router": "^8.3.0", "react-router": "^8.3.1",
"sonner": "^2.0.8", "sonner": "^2.0.8",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
"tailwindcss": "^4.3.3", "tailwindcss": "^4.3.3",
"zod": "^4.3.6" "zod": "^4.5.4"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@tanstack/eslint-plugin-query": "^5.102.8", "@tanstack/eslint-plugin-query": "^5.102.8",
"@types/node": "^26.4.0", "@types/node": "^26.4.1",
"@types/react": "^19.2.18", "@types/react": "^19.2.18",
"@types/react-dom": "^19.2.5", "@types/react-dom": "^19.2.7",
"@vitejs/plugin-react": "^6.1.1", "@vitejs/plugin-react": "^6.1.1",
"eslint": "^10.9.1", "eslint": "^10.9.1",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.5", "eslint-plugin-react-refresh": "^0.5.6",
"globals": "^17.11.0", "globals": "^17.12.0",
"rollup-plugin-visualizer": "^7.1.1", "rollup-plugin-visualizer": "^7.1.1",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "~6.0.2", "typescript": "~6.0.2",
"typescript-eslint": "^8.68.0", "typescript-eslint": "^8.69.0",
"vite": "^8.2.2" "vite": "^8.2.2"
} }
} }
+431 -343
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

+98 -98
View File
@@ -1,104 +1,104 @@
{ {
"loginTitle": "Welcome back, login with", "loginTitle": "Benvingut de nou, inicieu la sessió amb",
"loginTitleSimple": "Welcome back, please login", "loginTitleSimple": "Benvingut de nou, si us plau, inicieu sessió",
"loginDivider": "Or", "loginDivider": "O",
"loginUsername": "Username", "loginUsername": "Nom dusuari",
"loginPassword": "Password", "loginPassword": "Contrasenya",
"loginSubmit": "Login", "loginSubmit": "Iniciar sessió",
"loginFailTitle": "Failed to log in", "loginFailTitle": "No s'ha pogut iniciar sessió",
"loginFailSubtitle": "Please check your username and password", "loginFailSubtitle": "Si us plau, comproveu el vostre nom d'usuari i contrasenya",
"loginFailRateLimit": "You failed to login too many times. Please try again later", "loginFailRateLimit": "Heu fallat en iniciar sessió massa vegades. Si us plau, proveu-ho més tard",
"loginSuccessTitle": "Logged in", "loginSuccessTitle": "Heu iniciat sessió",
"loginSuccessSubtitle": "Welcome back!", "loginSuccessSubtitle": "Benvingut de nou!",
"loginOauthFailTitle": "An error occurred", "loginOauthFailTitle": "S'ha produït un error",
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "Errada en obtenir l'URL OAuth",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "Redirigint",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "Redirigint al vostre proveïdor OAuth",
"loginOauthAutoRedirectTitle": "OAuth Auto Redirect", "loginOauthAutoRedirectTitle": "Autoredirecció OAuth",
"loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", "loginOauthAutoRedirectSubtitle": "Sereu automàticament redirigits al vostre proveïdor OAuth per a autenticar-vos.",
"loginOauthAutoRedirectButton": "Redirect now", "loginOauthAutoRedirectButton": "Redirigeix ara",
"continueTitle": "Continue", "continueTitle": "Continuar",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "S'està redirigint...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "Hauríeu de ser redirigits aviat a l'app",
"continueRedirectManually": "Redirect me manually", "continueRedirectManually": "Redirigiu-me manualment",
"continueInsecureRedirectTitle": "Insecure redirect", "continueInsecureRedirectTitle": "Redirecció no segura",
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?", "continueInsecureRedirectSubtitle": "Esteu intentant redirigir des de <code>https</code>a<code>http</code> que no és segur. Esteu segurs de voler continuar?",
"continueUntrustedRedirectTitle": "Untrusted redirect", "continueUntrustedRedirectTitle": "Redirecció no confiable",
"continueUntrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{cookieDomain}}</code>). Are you sure you want to continue?", "continueUntrustedRedirectSubtitle": "Esteu intentant redirigir a un domini que no coincideix amb el vostre domini (<code>{{cookieDomain}}</code>). Esteu segurs de voler continuar?",
"logoutFailTitle": "Failed to log out", "logoutFailTitle": "Errada en tancar la sessió",
"logoutFailSubtitle": "Please try again", "logoutFailSubtitle": "Si us plau, torneu-ho a provar",
"logoutSuccessTitle": "Logged out", "logoutSuccessTitle": "Sessió tancada",
"logoutSuccessSubtitle": "You have been logged out", "logoutSuccessSubtitle": "S'ha tancat la sessió",
"logoutTitle": "Logout", "logoutTitle": "Tanca la sessió",
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.", "logoutUsernameSubtitle": "Heu iniciat sessió com a <code>{{username}}</code>. Cliqueu el botó de sota per a tancar la sessió.",
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.", "logoutOauthSubtitle": "Heu iniciat la sessió actualment com a <code>{{username}}</code> fent servir {{provider}} com a proveïdor OAuth. Cliqueu el botó de sota per a tancar la sessió.",
"notFoundTitle": "Page not found", "notFoundTitle": "Pàgina no trobada",
"notFoundSubtitle": "The page you are looking for does not exist.", "notFoundSubtitle": "La pàgina que cerques no existeix.",
"notFoundButton": "Go home", "notFoundButton": "Torna a l'inici",
"totpFailTitle": "Failed to verify code", "totpFailTitle": "Errada en verificar el codi",
"totpFailSubtitle": "Please check your code and try again", "totpFailSubtitle": "Si us plau, comprova el codi i torna-ho a provar",
"totpSuccessTitle": "Verified", "totpSuccessTitle": "Verificat",
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirigint a la teva app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Introdueixi el codi TOTP",
"totpSubtitle": "Please enter the code from your authenticator app.", "totpSubtitle": "Si us plau, introdueix el codi de la teva aplicació d'autenticació.",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "No autoritzat",
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.", "unauthorizedResourceSubtitle": "L'usuari amb el nom <code>{{username}}</code> no està autoritzat a accedir al recurs <code>{{resource}}</code>.",
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.", "unauthorizedLoginSubtitle": "L'usuari amb el nom <code>{{username}}</code> no està autoritzat a iniciar la sessió.",
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.", "unauthorizedGroupsSubtitle": "L'usuari amb el nom <code>{{username}}</code> no està als grups requerits per al recurs <code>{{resource}}</code>.",
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.", "unauthorizedIpSubtitle": "La vostra adreça IP <code>{{ip}}</code> no està autoritzada a accedir al recurs <code>{{resource}}</code>.",
"unauthorizedButton": "Try again", "unauthorizedButton": "Tornar-ho a provar",
"cancelTitle": "Cancel", "cancelTitle": "Cancel·lar",
"forgotPasswordTitle": "Forgot your password?", "forgotPasswordTitle": "Heu oblidat la vostra contrasenya?",
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", "failedToFetchProvidersTitle": "Errada en carregar els proveïdors d'autenticació. Si us plau, verifiqueu la vostra configuració.",
"errorTitle": "An error occurred", "errorTitle": "S'ha produït un error",
"errorSubtitleInfo": "The following error occurred while processing your request:", "errorSubtitleInfo": "Ha ocorregut l'error següent mentre es processava la vostra petició:",
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.",
"forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", "forgotPasswordMessage": "Podeu reiniciar la vostra contrasenya canviant la variable d'entorn `USERS`.",
"fieldRequired": "This field is required", "fieldRequired": "Aquest camp és obligatori",
"invalidInput": "Invalid input", "invalidInput": "Entrada no vàlida",
"domainWarningTitle": "Invalid Domain", "domainWarningTitle": "Domini invàlid",
"domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.", "domainWarningSubtitle": "Esteu visitant aquesta instància des d'un domini incorrecte. Si procediu, podríeu trobar-os amb problemes d'autenticació.",
"domainWarningCurrent": "Current:", "domainWarningCurrent": "Actual:",
"domainWarningExpected": "Expected:", "domainWarningExpected": "Esperat:",
"ignoreTitle": "Ignore", "ignoreTitle": "Ignorar",
"goToCorrectDomainTitle": "Go to correct domain", "goToCorrectDomainTitle": "Anar al domini correcte",
"authorizeTitle": "Authorize", "authorizeTitle": "Autoritzar",
"authorizeCardTitle": "Continue to {{app}}?", "authorizeCardTitle": "Continuar a {{app}}?",
"authorizeSubtitle": "Would you like to continue to this app? Please carefully review the permissions requested by the app.", "authorizeSubtitle": "Voldríeu continuar a aquesta app? Si us plau, reviseu amb cura els permisos sol·licitats per a l'app.",
"authorizeSubtitleOAuth": "Would you like to continue to this app?", "authorizeSubtitleOAuth": "Voldríeu continuar a aquesta app?",
"authorizeLoadingTitle": "Loading...", "authorizeLoadingTitle": "S'està carregant...",
"authorizeLoadingSubtitle": "Please wait while we load the client information.", "authorizeLoadingSubtitle": "Si us plau, espereu mentre carreguem la informació del client.",
"authorizeSuccessTitle": "Authorized", "authorizeSuccessTitle": "Autoritzat",
"authorizeSuccessSubtitle": "You will be redirected to the app in a few seconds.", "authorizeSuccessSubtitle": "Sereu redirigits a l'app en pocs segons.",
"authorizeErrorClientInfo": "An error occurred while loading the client information. Please try again later.", "authorizeErrorClientInfo": "Ha ocorregut un error mentre es carregava la informació del client. Si us plau, proveu-ho més tard.",
"authorizeErrorInvalidParams": "The request is missing required parameters or has invalid parameters. Please check the URL and try again.", "authorizeErrorInvalidParams": "La petició manca dels paràmetres requerits o té paràmetres invàlids. Si us plau, comproveu l'URL i torneu-ho a provar.",
"openidScopeName": "OpenID Connect", "openidScopeName": "OpenID Connect",
"openidScopeDescription": "Allows the app to access your OpenID Connect information.", "openidScopeDescription": "Permet a l'app l'accés a la vostra informació OpenID Connect.",
"emailScopeName": "Email", "emailScopeName": "Correu electrònic",
"emailScopeDescription": "Allows the app to access your email address.", "emailScopeDescription": "Permet a l'app l'accés a la vostra adreça de correu electrònic.",
"profileScopeName": "Profile", "profileScopeName": "Perfil",
"profileScopeDescription": "Allows the app to access your profile information.", "profileScopeDescription": "Permet a l'app l'accés a la vostra informació de perfil.",
"groupsScopeName": "Groups", "groupsScopeName": "Grups",
"groupsScopeDescription": "Allows the app to access your group information.", "groupsScopeDescription": "Permet a l'app l'accés la vostra informació de grup.",
"backToLoginButton": "Back to login", "backToLoginButton": "Tornar a l'inici de sessió",
"phoneScopeName": "Phone", "phoneScopeName": "Telèfon",
"phoneScopeDescription": "Allows the app to access your phone number.", "phoneScopeDescription": "Permet a l'app l'accés al vostre número de telèfon.",
"addressScopeName": "Address", "addressScopeName": "Adreça",
"addressScopeDescription": "Allows the app to access your address.", "addressScopeDescription": "Permet a l'app l'accés a la vostra adreça.",
"loginTailscaleTitle": "Continue with Tailscale", "loginTailscaleTitle": "Continuar amb Tailscale",
"loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?", "loginTailscaleDescription": "Sembla que esteu accedint a Tinyauth des d'un dispositiu Tailscale autoritzat. Voldríeu continuar amb la vostra connexió Tailscale?",
"loginTailscaleDeviceName": "Device name:", "loginTailscaleDeviceName": "Nom del dispositiu:",
"loginTailscaleOtherMethod": "Login with another method", "loginTailscaleOtherMethod": "Iniciar la sessió amb un altre mètode",
"loginTailscaleSuccess": "Successfully authenticated with Tailscale.", "loginTailscaleSuccess": "Autenticat correctament amb Tailscale.",
"loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.", "loginTailscaleFail": "Errada en autentificar amb Tailscale. Si us plau, intenteu-ho una altra vegada o feu servir un altre mètode d'autenticació.",
"logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device <code>{{deviceName}}</code>. Click the button below to logout.", "logoutTailscaleSubtitle": "Heu iniciat sessió actualment amb Tailscale en el vostre dispositiu <code>{{deviceName}}</code>. Cliqueu el botó de sota per a tancar la sessió.",
"quickActionsLanguage": "Language", "quickActionsLanguage": "Idioma",
"quickActionsTheme": "Theme", "quickActionsTheme": "Tema",
"quickActionsThemeLight": "Light", "quickActionsThemeLight": "Clar",
"quickActionsThemeDark": "Dark", "quickActionsThemeDark": "Fosc",
"quickActionsThemeSystem": "System", "quickActionsThemeSystem": "Sistema",
"quickActionsLogout": "Logout", "quickActionsLogout": "Sortir de la sessió",
"quickActionsTitle": "Quick Actions", "quickActionsTitle": "Accions ràpides",
"quickActionsProviderLocal": "Local", "quickActionsProviderLocal": "Local",
"quickActionsProviderLDAP": "LDAP", "quickActionsProviderLDAP": "LDAP",
"quickActionsProviderOAuth": "{{provider}} OAuth" "quickActionsProviderOAuth": "{{provider}} OAuth"
+5 -5
View File
@@ -8,7 +8,7 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 github.com/cenkalti/backoff/v5 v5.0.3
github.com/docker/docker v28.5.2+incompatible github.com/docker/docker v28.5.2+incompatible
github.com/gin-gonic/gin v1.12.0 github.com/gin-gonic/gin v1.12.0
github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-jose/go-jose/v4 v4.1.5
github.com/go-ldap/ldap/v3 v3.4.14 github.com/go-ldap/ldap/v3 v3.4.14
github.com/golang-jwt/jwt/v5 v5.3.1 github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang-migrate/migrate/v4 v4.19.1 github.com/golang-migrate/migrate/v4 v4.19.1
@@ -23,14 +23,14 @@ require (
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298 github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
github.com/weppos/publicsuffix-go v0.50.3 github.com/weppos/publicsuffix-go v0.50.3
go.uber.org/dig v1.19.0 go.uber.org/dig v1.19.0
golang.org/x/crypto v0.55.0 golang.org/x/crypto v0.56.0
golang.org/x/net v0.58.0 golang.org/x/net v0.58.0
golang.org/x/oauth2 v0.36.0 golang.org/x/oauth2 v0.36.0
golang.org/x/tools v0.49.0 golang.org/x/tools v0.49.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.37.0 k8s.io/apimachinery v0.37.0
k8s.io/client-go v0.37.0 k8s.io/client-go v0.37.0
modernc.org/sqlite v1.57.0 modernc.org/sqlite v1.58.0
) )
require ( require (
@@ -145,9 +145,9 @@ require (
k8s.io/klog/v2 v2.140.0 // indirect k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect
modernc.org/libc v1.74.4 // indirect modernc.org/libc v1.75.6 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.12.1 // indirect
rsc.io/qr v0.2.0 // indirect rsc.io/qr v0.2.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
+16 -16
View File
@@ -115,8 +115,8 @@ github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ= github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs= github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA= github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -355,8 +355,8 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74=
@@ -410,30 +410,30 @@ k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad h1:oXImqH8mQNk7PmvzKhmN3d
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I= k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I=
k8s.io/utils v0.0.0-20260626114624-be93311217bd h1:Ea7fgQ5we8Y9T0OX5o0dAHzQOBRI07D/dEYRaB9ZZEs= k8s.io/utils v0.0.0-20260626114624-be93311217bd h1:Ea7fgQ5we8Y9T0OX5o0dAHzQOBRI07D/dEYRaB9ZZEs=
k8s.io/utils v0.0.0-20260626114624-be93311217bd/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= k8s.io/utils v0.0.0-20260626114624-be93311217bd/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI= modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU= modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk= modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI= modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k= modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co= modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg= modernc.org/sqlite v1.58.0 h1:38u40/bwkfM7f0Myhosl+SEMltSDxnGdQf8o6Kjmys0=
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ= modernc.org/sqlite v1.58.0/go.mod h1:rsD2CckafgObKC4DhBlGBf+RiHxkc3hINGt1Xw32tVY=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+5 -1
View File
@@ -1,6 +1,8 @@
package controller package controller
import ( import (
"crypto/sha256"
"crypto/subtle"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -545,7 +547,9 @@ func (controller *OIDCController) Token(c *gin.Context) {
return return
} }
if client.ClientSecret != creds.ClientSecret { clientSecretHash := sha256.Sum256([]byte(client.ClientSecret))
providedSecretHash := sha256.Sum256([]byte(creds.ClientSecret))
if subtle.ConstantTimeCompare(clientSecretHash[:], providedSecretHash[:]) != 1 {
controller.log.App.Warn().Str("clientId", creds.ClientID).Msg("Invalid client secret") controller.log.App.Warn().Str("clientId", creds.ClientID).Msg("Invalid client secret")
c.JSON(400, gin.H{ c.JSON(400, gin.H{
"error": "invalid_client", "error": "invalid_client",
+1 -1
View File
@@ -53,7 +53,7 @@ func NewDefaultConfiguration(runtimeEnv RuntimeEnv) *Config {
UI: UIConfig{ UI: UIConfig{
Title: "Tinyauth", Title: "Tinyauth",
ForgotPasswordMessage: "You can change your password by changing the configuration.", ForgotPasswordMessage: "You can change your password by changing the configuration.",
BackgroundImage: "/background.jpg", BackgroundImage: "/background.webp",
WarningsEnabled: true, WarningsEnabled: true,
}, },
LDAP: LDAPConfig{ LDAP: LDAPConfig{
+65 -13
View File
@@ -2,8 +2,12 @@ package service
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os"
"time"
"github.com/cenkalti/backoff/v5"
"github.com/steveiliop56/ding" "github.com/steveiliop56/ding"
"github.com/tinyauthapp/tinyauth/internal/model" "github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/utils/decoders" "github.com/tinyauthapp/tinyauth/internal/utils/decoders"
@@ -14,6 +18,10 @@ import (
"github.com/docker/docker/client" "github.com/docker/docker/client"
) )
var (
ErrPingFailed = fmt.Errorf("failed to ping docker")
)
type DockerService struct { type DockerService struct {
log *logger.Logger log *logger.Logger
client *client.Client client *client.Client
@@ -31,24 +39,52 @@ type DockerServiceInput struct {
} }
func NewDockerService(i DockerServiceInput) (*DockerService, error) { func NewDockerService(i DockerServiceInput) (*DockerService, error) {
client, err := client.NewClientWithOpts(client.FromEnv) service := &DockerService{
log: i.Log,
context: i.Ctx,
}
service.log.App.Debug().Msg("Attempting to connect to Docker")
if os.Getenv("DOCKER_HOST") == "" {
cli, err := service.connect()
if err != nil {
if errors.Is(err, ErrPingFailed) {
service.log.App.Debug().Msg("Docker not connected")
return nil, nil
}
return nil, fmt.Errorf("failed to connect to docker: %w", err)
}
service.client = cli
} else {
exp := backoff.NewExponentialBackOff()
exp.InitialInterval = 3 * time.Second
exp.RandomizationFactor = 0.1
exp.Multiplier = 1.5
exp.Reset()
operation := func() (*client.Client, error) {
if service.client != nil {
service.client.Close()
}
cli, err := service.connect()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return cli, nil
client.NegotiateAPIVersion(i.Ctx)
_, err = client.Ping(i.Ctx)
if err != nil {
i.Log.App.Debug().Err(err).Msg("Docker not connected")
return nil, nil
} }
service := &DockerService{ cli, err := backoff.Retry(service.context, operation, backoff.WithBackOff(exp), backoff.WithMaxTries(3))
log: i.Log,
client: client, if err != nil {
context: i.Ctx, if errors.Is(err, ErrPingFailed) {
service.log.App.Debug().Msg("Docker not connected after retrying")
return nil, nil
}
return nil, fmt.Errorf("failed to connect to docker after retrying: %w", err)
}
service.client = cli
} }
service.isConnected = true service.isConnected = true
@@ -59,6 +95,22 @@ func NewDockerService(i DockerServiceInput) (*DockerService, error) {
return service, nil return service, nil
} }
func (docker *DockerService) connect() (*client.Client, error) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return nil, err
}
_, err = cli.Ping(docker.context)
if err != nil {
return nil, ErrPingFailed
}
return cli, nil
}
func (docker *DockerService) getContainers() ([]container.Summary, error) { func (docker *DockerService) getContainers() ([]container.Summary, error) {
return docker.client.ContainerList(docker.context, container.ListOptions{}) return docker.client.ContainerList(docker.context, container.ListOptions{})
} }
+2 -1
View File
@@ -6,6 +6,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256" "crypto/sha256"
"crypto/subtle"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
@@ -882,7 +883,7 @@ func (service *OIDCService) ValidatePKCE(codeChallenge string, codeVerifier stri
if codeChallenge == "" { if codeChallenge == "" {
return true return true
} }
return codeChallenge == service.hashAndEncodePKCE(codeVerifier) return subtle.ConstantTimeCompare([]byte(codeChallenge), []byte(service.hashAndEncodePKCE(codeVerifier))) == 1
} }
func (service *OIDCService) hashAndEncodePKCE(codeVerifier string) string { func (service *OIDCService) hashAndEncodePKCE(codeVerifier string) string {
+1 -1
View File
@@ -18,7 +18,7 @@ func CreateTestConfigs(t *testing.T) (model.Config, model.RuntimeConfig) {
UI: model.UIConfig{ UI: model.UIConfig{
Title: "Tinyauth Test", Title: "Tinyauth Test",
ForgotPasswordMessage: "foo", ForgotPasswordMessage: "foo",
BackgroundImage: "/background.jpg", BackgroundImage: "/background.webp",
WarningsEnabled: true, WarningsEnabled: true,
}, },
OAuth: model.OAuthConfig{ OAuth: model.OAuthConfig{