Compare commits

...

10 Commits

Author SHA1 Message Date
Elias Schneider
a879bfa418 release: 0.27.1 2025-01-24 12:07:10 +01:00
Elias Schneider
164ce6a3d7 fix: add __HOST prefix to cookies (#175) 2025-01-24 12:01:27 +01:00
Chris Danis
ef1aeb7152 docs: make CONTRIBUTING instructions work & fix example envs (#152) 2025-01-24 10:51:26 +01:00
Elias Schneider
47c39f6d38 fix: use OS hostname for SMTP EHLO message 2025-01-24 10:36:16 +01:00
Elias Schneider
2884021055 chore: remove duplicate text from issue template 2025-01-23 19:57:50 +01:00
Kyle Mendell
def39b8703 chore: bug template update (#133)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
2025-01-23 19:10:48 +01:00
Elias Schneider
d071641890 docs: remove duplicate contribute.md 2025-01-23 18:10:28 +01:00
Elias Schneider
397544c0f3 fix: send hostname derived from PUBLIC_APP_URL with SMTP EHLO command 2025-01-23 17:55:36 +01:00
Kyle Mendell
1fb99e5d52 docs: add more client-examples (#166) 2025-01-23 11:37:41 +01:00
Elias Schneider
7b403552ba chore: add GitHub release creation to create-release.sh script 2025-01-23 08:56:28 +01:00
38 changed files with 364 additions and 135 deletions

View File

@@ -34,4 +34,23 @@ body:
- type: markdown
attributes:
value: |
Before submitting, please check if the issues hasn't been raised before.
### Additional Information
- type: textarea
id: extra-information
validations:
required: true
attributes:
label: "Version and Environment"
description: "Please specify the version of Pocket ID, along with any environment-specific configurations, such your reverse proxy, that might be relevant."
placeholder: "e.g., v0.24.1"
- type: textarea
id: log-files
validations:
required: false
attributes:
label: "Log Output"
description: "Output of log files when the issue occured to help us diagnose the issue."
- type: markdown
attributes:
value: |
**Before submitting, please check if the issue hasn't been raised before.**

View File

@@ -1 +1 @@
0.27.0
0.27.1

View File

@@ -1,3 +1,12 @@
## [](https://github.com/stonith404/pocket-id/compare/v0.27.0...v) (2025-01-24)
### Bug Fixes
* add `__HOST` prefix to cookies ([#175](https://github.com/stonith404/pocket-id/issues/175)) ([164ce6a](https://github.com/stonith404/pocket-id/commit/164ce6a3d7fa8ae5275c94302952cf318e3b3113))
* send hostname derived from `PUBLIC_APP_URL` with SMTP EHLO command ([397544c](https://github.com/stonith404/pocket-id/commit/397544c0f3f2b49f1f34ae53e6b9daf194d1ae28))
* use OS hostname for SMTP EHLO message ([47c39f6](https://github.com/stonith404/pocket-id/commit/47c39f6d382c496cb964262adcf76cc8dbb96da3))
## [](https://github.com/stonith404/pocket-id/compare/v0.26.0...v) (2025-01-22)

View File

@@ -55,14 +55,14 @@ The frontend is built with [SvelteKit](https://kit.svelte.dev) and written in Ty
3. Install the dependencies with `npm install`
4. Start the frontend with `npm run dev`
You're all set!
### Reverse Proxy
We use [Caddy](https://caddyserver.com) as a reverse proxy. You can use any other reverse proxy if you want but you have to configure it yourself.
#### Setup
Run `caddy run --config reverse-proxy/Caddyfile` in the root folder.
You're all set!
### Testing
We are using [Playwright](https://playwright.dev) for end-to-end testing.

View File

@@ -1,6 +1,8 @@
APP_ENV=production
PUBLIC_APP_URL=http://localhost
# /!\ If PUBLIC_APP_URL is not a localhost address, it must be HTTPS
DB_PROVIDER=sqlite
# MAXMIND_LICENSE_KEY=fixme # needed for IP geolocation in the audit log
SQLITE_DB_PATH=data/pocket-id.db
POSTGRES_CONNECTION_STRING=postgresql://postgres:postgres@localhost:5432/pocket-id
UPLOAD_PATH=data/uploads

View File

@@ -6,9 +6,10 @@ import (
)
func Bootstrap() {
initApplicationImages()
db := newDatabase()
appConfigService := service.NewAppConfigService(db)
initApplicationImages()
initRouter(db, appConfigService)
}

View File

@@ -1,7 +1,9 @@
package controller
import (
"github.com/stonith404/pocket-id/backend/internal/utils/cookie"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
@@ -184,7 +186,10 @@ func (uc *UserController) exchangeOneTimeAccessTokenHandler(c *gin.Context) {
return
}
utils.AddAccessTokenCookie(c, uc.appConfigService.DbConfig.SessionDuration.Value, token)
sessionDurationInMinutesParsed, _ := strconv.Atoi(uc.appConfigService.DbConfig.SessionDuration.Value)
maxAge := sessionDurationInMinutesParsed * 60
cookie.AddAccessTokenCookie(c, maxAge, token)
c.JSON(http.StatusOK, userDto)
}
@@ -201,7 +206,10 @@ func (uc *UserController) getSetupAccessTokenHandler(c *gin.Context) {
return
}
utils.AddAccessTokenCookie(c, uc.appConfigService.DbConfig.SessionDuration.Value, token)
sessionDurationInMinutesParsed, _ := strconv.Atoi(uc.appConfigService.DbConfig.SessionDuration.Value)
maxAge := sessionDurationInMinutesParsed * 60
cookie.AddAccessTokenCookie(c, maxAge, token)
c.JSON(http.StatusOK, userDto)
}

View File

@@ -5,8 +5,9 @@ import (
"github.com/stonith404/pocket-id/backend/internal/common"
"github.com/stonith404/pocket-id/backend/internal/dto"
"github.com/stonith404/pocket-id/backend/internal/middleware"
"github.com/stonith404/pocket-id/backend/internal/utils"
"github.com/stonith404/pocket-id/backend/internal/utils/cookie"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
@@ -42,12 +43,12 @@ func (wc *WebauthnController) beginRegistrationHandler(c *gin.Context) {
return
}
c.SetCookie("session_id", options.SessionID, int(options.Timeout.Seconds()), "/", "", true, true)
cookie.AddSessionIdCookie(c, int(options.Timeout.Seconds()), options.SessionID)
c.JSON(http.StatusOK, options.Response)
}
func (wc *WebauthnController) verifyRegistrationHandler(c *gin.Context) {
sessionID, err := c.Cookie("session_id")
sessionID, err := c.Cookie(cookie.SessionIdCookieName)
if err != nil {
c.Error(&common.MissingSessionIdError{})
return
@@ -76,12 +77,12 @@ func (wc *WebauthnController) beginLoginHandler(c *gin.Context) {
return
}
c.SetCookie("session_id", options.SessionID, int(options.Timeout.Seconds()), "/", "", true, true)
cookie.AddSessionIdCookie(c, int(options.Timeout.Seconds()), options.SessionID)
c.JSON(http.StatusOK, options.Response)
}
func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) {
sessionID, err := c.Cookie("session_id")
sessionID, err := c.Cookie(cookie.SessionIdCookieName)
if err != nil {
c.Error(&common.MissingSessionIdError{})
return
@@ -105,7 +106,10 @@ func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) {
return
}
utils.AddAccessTokenCookie(c, wc.appConfigService.DbConfig.SessionDuration.Value, token)
sessionDurationInMinutesParsed, _ := strconv.Atoi(wc.appConfigService.DbConfig.SessionDuration.Value)
maxAge := sessionDurationInMinutesParsed * 60
cookie.AddAccessTokenCookie(c, maxAge, token)
c.JSON(http.StatusOK, userDto)
}
@@ -165,6 +169,6 @@ func (wc *WebauthnController) updateCredentialHandler(c *gin.Context) {
}
func (wc *WebauthnController) logoutHandler(c *gin.Context) {
utils.AddAccessTokenCookie(c, "0", "")
cookie.AddAccessTokenCookie(c, 0, "")
c.Status(http.StatusNoContent)
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stonith404/pocket-id/backend/internal/common"
"github.com/stonith404/pocket-id/backend/internal/service"
"github.com/stonith404/pocket-id/backend/internal/utils/cookie"
"strings"
)
@@ -19,7 +20,7 @@ func NewJwtAuthMiddleware(jwtService *service.JwtService, ignoreUnauthenticated
func (m *JwtAuthMiddleware) Add(adminOnly bool) gin.HandlerFunc {
return func(c *gin.Context) {
// Extract the token from the cookie or the Authorization header
token, err := c.Cookie("access_token")
token, err := c.Cookie(cookie.AccessTokenCookieName)
if err != nil {
authorizationHeaderSplitted := strings.Split(c.GetHeader("Authorization"), " ")
if len(authorizationHeaderSplitted) == 2 {

View File

@@ -14,6 +14,7 @@ import (
"net"
"net/smtp"
"net/textproto"
"os"
ttemplate "text/template"
"time"
)
@@ -99,7 +100,7 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T
smtpAddress := srv.appConfigService.DbConfig.SmtpHost.Value + ":" + port
var client *smtp.Client
if srv.appConfigService.DbConfig.SmtpTls.Value == "false" {
client, err = smtp.Dial(smtpAddress)
client, err = srv.connectToSmtpServer(smtpAddress)
} else if port == "465" {
client, err = srv.connectToSmtpServerUsingImplicitTLS(
smtpAddress,
@@ -115,9 +116,16 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T
if err != nil {
return fmt.Errorf("failed to connect to SMTP server: %w", err)
}
defer client.Close()
// Set the hello message manually as for example Google rejects the default "localhost" value
hostname, err := os.Hostname()
if err == nil {
if err := client.Hello(hostname); err != nil {
return fmt.Errorf("failed to say hello to SMTP server: %w", err)
}
}
smtpUser := srv.appConfigService.DbConfig.SmtpUser.Value
smtpPassword := srv.appConfigService.DbConfig.SmtpPassword.Value
@@ -141,6 +149,15 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T
return nil
}
func (srv *EmailService) connectToSmtpServer(serverAddr string) (*smtp.Client, error) {
conn, err := netDialer.Dial("tcp", serverAddr)
if err != nil {
return nil, fmt.Errorf("failed to connect to SMTP server: %w", err)
}
client, err := smtp.NewClient(conn, srv.appConfigService.DbConfig.SmtpHost.Value)
return client, err
}
func (srv *EmailService) connectToSmtpServerUsingImplicitTLS(serverAddr string, tlsConfig *tls.Config) (*smtp.Client, error) {
tlsDialer := &tls.Dialer{
NetDialer: netDialer,

View File

@@ -0,0 +1,13 @@
package cookie
import (
"github.com/gin-gonic/gin"
)
func AddAccessTokenCookie(c *gin.Context, maxAgeInSeconds int, token string) {
c.SetCookie(AccessTokenCookieName, token, maxAgeInSeconds, "/", "", true, true)
}
func AddSessionIdCookie(c *gin.Context, maxAgeInSeconds int, sessionID string) {
c.SetCookie(SessionIdCookieName, sessionID, maxAgeInSeconds, "/", "", true, true)
}

View File

@@ -0,0 +1,16 @@
package cookie
import (
"github.com/stonith404/pocket-id/backend/internal/common"
"strings"
)
var AccessTokenCookieName = "__Host-access_token"
var SessionIdCookieName = "__Host-session"
func init() {
if strings.HasPrefix(common.EnvConfig.AppURL, "http://") {
AccessTokenCookieName = "access_token"
SessionIdCookieName = "session"
}
}

View File

@@ -1,12 +0,0 @@
package utils
import (
"github.com/gin-gonic/gin"
"strconv"
)
func AddAccessTokenCookie(c *gin.Context, sessionDurationInMinutes string, token string) {
sessionDurationInMinutesParsed, _ := strconv.Atoi(sessionDurationInMinutes)
maxAge := sessionDurationInMinutesParsed * 60
c.SetCookie("access_token", token, maxAge, "/", "", true, true)
}

View File

@@ -0,0 +1,22 @@
---
id: grist
---
# Grist
## Pocket ID Setup
1. In Pocket-ID create a new OIDC Client, name it i.e. `Grist`
2. Set the callback url to: `https://<Grist Host>/oauth2/callback`
3. In Grist (Docker/Docker Compose/etc), set these environment variables:
```ini
GRIST_OIDC_IDP_ISSUER="https://<Pocket ID Host>/.well-known/openid-configuration"
GRIST_OIDC_IDP_CLIENT_ID="<Client ID from the OIDC Client created in Pocket ID>"
GRIST_OIDC_IDP_CLIENT_SECRET="<Client Secret from the OIDC Client created in Pocket ID>"
GRIST_OIDC_SP_HOST="https://<Grist Host>"
GRIST_OIDC_IDP_SCOPES="openid email profile" # Default
GRIST_OIDC_IDP_SKIP_END_SESSION_ENDPOINT=true # Default=false, needs to be true for Pocket Id b/c end_session_endpoint is not implemented
GRIST_OIDC_IDP_END_SESSION_ENDPOINT="https://<Pocket ID Host>/api/webauthn/logout" # Only set this if GRIST_OIDC_IDP_SKIP_END_SESSION_ENDPOINT=false and you need to define a custom endpoint
```
4. Also ensure that the `GRIST_DEFAULT_EMAIL` env variable is set to the same email address as your user profile within Pocket ID
5. Start/Restart Grist

View File

@@ -0,0 +1,46 @@
---
id: netbox
---
# Netbox
**This guide does not currently show how to map groups in netbox from OIDC claims**
The following example variables are used, and should be replaced with your actual URLS.
- netbox.example.com (The url of your netbox instance.)
- id.example.com (The url of your Pocket ID instance.)
## Pocket ID Setup
1. In Pocket-ID create a new OIDC Client, name it i.e. `Netbox`.
2. Set a logo for this OIDC Client if you would like too.
3. Set the callback URL to: `https://netbox.example.com/oauth/complete/oidc/`.
4. Copy the `Client ID`, and the `Client Secret` for use in the next steps.
## Netbox Setup
This guide assumes you are using the git based install of netbox.
1. On your netbox server navigate to `/opt/netbox/netbox/netbox`
2. Add the following to your `configuration.py` file:
```python
# Remote authentication support
REMOTE_AUTH_ENABLED = True
REMOTE_AUTH_BACKEND = 'social_core.backends.open_id_connect.OpenIdConnectAuth'
REMOTE_AUTH_HEADER = 'HTTP_REMOTE_USER'
REMOTE_AUTH_USER_FIRST_NAME = 'HTTP_REMOTE_USER_FIRST_NAME'
REMOTE_AUTH_USER_LAST_NAME = 'HTTP_REMOTE_USER_LAST_NAME'
REMOTE_AUTH_USER_EMAIL = 'HTTP_REMOTE_USER_EMAIL'
REMOTE_AUTH_AUTO_CREATE_USER = True
REMOTE_AUTH_DEFAULT_GROUPS = []
REMOTE_AUTH_DEFAULT_PERMISSIONS = {}
SOCIAL_AUTH_OIDC_ENDPOINT = 'https://id.example.com'
SOCIAL_AUTH_OIDC_KEY = '<client id from the first part of this guide>'
SOCIAL_AUTH_OIDC_SECRET = '<client id from the first part of this guide>'
LOGOUT_REDIRECT_URL = 'https://netbox.example.com'
```
3. Save the file and restart netbox: `sudo systemctl start netbox netbox-rq`

View File

@@ -0,0 +1,42 @@
---
id: pgadmin
---
# pgAdmin
The following example variables are used, and should be replaced with your actual URLS.
- pgadmin.example.com (The url of your pgAdmin instance.)
- id.example.com (The url of your Pocket ID instance.)
## Pocket ID Setup
1. In Pocket-ID create a new OIDC Client, name it i.e. `pgAdmin`.
2. Set a logo for this OIDC Client if you would like too.
3. Set the callback URL to: `https://pgadmin.example.com/oauth2/authorize`.
4. Copy the `Client ID`, `Client Secret`, `Authorization URL`, `Userinfo URL`, `Token URL`, and `OIDC Discovery URL` for use in the next steps.
# pgAdmin Setup
1. Add the following to the `config_local.py` file for pgAdmin:
**Make sure to replace https://id.example.com with your actual Pocket ID URL**
```python
AUTHENTICATION_SOURCES = ['oauth2', 'internal'] # This keeps internal authentication enabled as well as oauth2
OAUTH2_AUTO_CREATE_USER = True
OAUTH2_CONFIG = [{
'OAUTH2_NAME' : 'pocketid',
'OAUTH2_DISPLAY_NAME' : 'Pocket ID',
'OAUTH2_CLIENT_ID' : '<client id from the earlier step>',
'OAUTH2_CLIENT_SECRET' : '<client secret from the earlier step>',
'OAUTH2_TOKEN_URL' : 'https://id.example.com/api/oidc/token',
'OAUTH2_AUTHORIZATION_URL' : 'https://id.example/authorize',
'OAUTH2_API_BASE_URL' : 'https://id.example.com',
'OAUTH2_USERINFO_ENDPOINT' : 'https://id.example.com/api/oidc/userinfo',
'OAUTH2_SERVER_METADATA_URL' : 'https://id.example.com/.well-known/openid-configuration',
'OAUTH2_SCOPE' : 'openid email profile',
'OAUTH2_ICON' : 'fa-openid',
'OAUTH2_BUTTON_COLOR' : '#fd4b2d' # Can select any color you would like here.
}]
```

View File

@@ -0,0 +1,38 @@
---
id: portainer
---
# Portainer
**This requires Portainers Business Edition**
The following example variables are used, and should be replaced with your actual URLS.
- portainer.example.com (The url of your Portainer instance.)
- id.example.com (The url of your Pocket ID instance.)
## Pocket ID Setup
1. In Pocket-ID create a new OIDC Client, name it i.e. `Portainer`.
2. Set a logo for this OIDC Client if you would like too.
3. Set the callback URL to: `https://portainer.example.com/`.
4. Copy the `Client ID`, `Client Secret`, `Authorization URL`, `Userinfo URL`, and `Token URL` for use in the next steps.
# Portainer Setup
- While initally setting up OAuth in Portainer, its recommended to keep the `Hide internal authentication prompt` set to `Off` incase you need a fallback login
- This guide does **NOT** cover how to setup group claims in Portainer.
1. Open the Portainer web interface and navigate to: `Settings > Authentication`
2. Select `Custom OAuth Provider`
3. Paste the `Client ID` from Pocket ID into the `Client ID` field in Portainer.
4. Paste the `Client Secret` from Pocket ID into the `Client Secret` field in Portainer.
5. Paste the `Authorization URL` from Pocket ID into the `Authorization URL` field in Portainer.
6. Paste the `Token URL` from Pocket ID into the `Access token URL` field in Portainer.
7. Paste the `Userinfo URL` from Pocket ID into the `Resource URL` field in Portainer.
8. Set the `Redirect URL` to `https://portainer.example.com`
9. Set the `Logout URL` to `https://portainer.example.com`
10. Set the `User identifier` field to `preferred_username`. (This will use the users username vs the email)
11. Set the `Scopes` field to: `email openid profile`
12. Set `Auth Style` to `Auto detect`
13. Save the settings and test the new OAuth Login.

View File

@@ -0,0 +1,30 @@
---
id: proxmox
---
# Proxmox
The following example variables are used, and should be replaced with your actual URLS.
- proxmox.example.com (The url of your proxmox instance.)
- id.example.com (The url of your Pocket ID instance.)
## Pocket ID Setup
1. In Pocket-ID create a new OIDC Client, name it i.e. `Proxmox`.
2. Set a logo for this OIDC Client if you would like too.
3. Set the callback URL to: `https://proxmox.example.com`.
4. Copy the `Client ID`, and the `Client Secret` for use in the next steps.
## Proxmox Setup
1. Open the Proxmox Console and navigate to: `Datacenter - Realms`
2. Add a new `Open ID Connect Server` Realm
3. Enter `https://id.example.com` for the `Issuer URL`
4. Enter a name for the realm of your choice ie. `PocketID`
5. Paste the `Client ID` from Pocket ID into the `Client ID` field in Proxmox.
6. Paste the `Client Secret` from Pocket ID into the `Client Key` field in Proxmox.
7. You can check the `Default` box if you want this to be the deafult realm proxmox uses when signing in.
8. Check the `Autocreate Users` checkbox. (This will automaitcally create users in Proxmox if they dont exsist.)
9. Select `username` for the `Username Claim` dropdown (This is personal preference and controls how the username is shown, ie: `username = username@PocketID` or `email = username@example@PocketID`).
10. Leave the rest as defaults and click `OK` to save the new realm.

View File

@@ -1,77 +0,0 @@
---
id: contribute
---
# Contributing
I am happy that you want to contribute to Pocket ID and help to make it better! All contributions are welcome, including issues, suggestions, pull requests and more.
## Getting started
You've found a bug, have suggestion or something else, just create an issue on GitHub and we can get in touch.
## Submit a Pull Request
Before you submit the pull request for review please ensure that
- The pull request naming follows the [Conventional Commits specification](https://www.conventionalcommits.org):
`<type>[optional scope]: <description>`
example:
```
feat(share): add password protection
```
Where `TYPE` can be:
- **feat** - is a new feature
- **doc** - documentation only changes
- **fix** - a bug fix
- **refactor** - code change that neither fixes a bug nor adds a feature
- Your pull request has a detailed description
- You run `npm run format` to format the code
## Setup project
Pocket ID consists of a frontend, backend and a reverse proxy.
### Backend
The backend is built with [Gin](https://gin-gonic.com) and written in Go.
#### Setup
1. Open the `backend` folder
2. Copy the `.env.example` file to `.env` and change the `APP_ENV` to `development`
3. Start the backend with `go run cmd/main.go`
### Frontend
The frontend is built with [SvelteKit](https://kit.svelte.dev) and written in TypeScript.
#### Setup
1. Open the `frontend` folder
2. Copy the `.env.example` file to `.env`
3. Install the dependencies with `npm install`
4. Start the frontend with `npm run dev`
You're all set!
### Reverse Proxy
We use [Caddy](https://caddyserver.com) as a reverse proxy. You can use any other reverse proxy if you want but you have to configure it yourself.
#### Setup
Run `caddy run --config reverse-proxy/Caddyfile` in the root folder.
### Testing
We are using [Playwright](https://playwright.dev) for end-to-end testing.
The tests can be run like this:
1. Start the backend normally
2. Start the frontend in production mode with `npm run build && node build/index.js`
3. Run the tests with `npm run test`

View File

@@ -59,12 +59,17 @@ const sidebars: SidebarsConfig = {
slug: "client-examples",
},
items: [
"client-examples/cloudflare-zero-trust",
"client-examples/grist",
"client-examples/hoarder",
"client-examples/jellyfin",
"client-examples/vikunja",
"client-examples/netbox",
"client-examples/open-webui",
"client-examples/pgadmin",
"client-examples/portainer",
"client-examples/proxmox",
"client-examples/semaphore-ui",
"client-examples/cloudflare-zero-trust",
"client-examples/vikunja",
],
},
{
@@ -82,8 +87,9 @@ const sidebars: SidebarsConfig = {
label: "Helping Out",
items: [
{
type: "doc",
id: "help-out/contribute",
type: "link",
label: "Contributing",
href: "https://github.com/stonith404/pocket-id/blob/main/CONTRIBUTING.md",
},
],
},

View File

@@ -1,2 +1,3 @@
PUBLIC_APP_URL=http://localhost
# /!\ If PUBLIC_APP_URL is not a localhost address, it must be HTTPS
INTERNAL_BACKEND_URL=http://localhost:8080

View File

@@ -1,6 +1,6 @@
{
"name": "pocket-id-frontend",
"version": "0.27.0",
"version": "0.27.1",
"private": true,
"scripts": {
"dev": "vite dev --port 3000",

View File

@@ -1,4 +1,5 @@
import { env } from '$env/dynamic/private';
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import type { Handle, HandleServerError } from '@sveltejs/kit';
import { AxiosError } from 'axios';
import jwt from 'jsonwebtoken';
@@ -9,7 +10,7 @@ import jwt from 'jsonwebtoken';
process.env.INTERNAL_BACKEND_URL = env.INTERNAL_BACKEND_URL ?? 'http://localhost:8080';
export const handle: Handle = async ({ event, resolve }) => {
const accessToken = event.cookies.get('access_token');
const accessToken = event.cookies.get(ACCESS_TOKEN_COOKIE_NAME);
let isSignedIn: boolean = false;
let isAdmin: boolean = false;

View File

@@ -0,0 +1,2 @@
export const HTTPS_ENABLED = process.env.PUBLIC_APP_URL?.startsWith('https://') ?? false;
export const ACCESS_TOKEN_COOKIE_NAME = HTTPS_ENABLED ? '__Host-access_token' : 'access_token';

View File

@@ -1,10 +1,11 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AppConfigService from '$lib/services/app-config-service';
import UserService from '$lib/services/user-service';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ cookies }) => {
const userService = new UserService(cookies.get('access_token'));
const appConfigService = new AppConfigService(cookies.get('access_token'));
const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const appConfigService = new AppConfigService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const user = await userService
.getCurrent()

View File

@@ -1,9 +1,10 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OidcService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ url, cookies }) => {
const clientId = url.searchParams.get('client_id');
const oidcService = new OidcService(cookies.get('access_token'));
const oidcService = new OidcService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const client = await oidcService.getClient(clientId!);

View File

@@ -1,10 +1,11 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service';
import WebAuthnService from '$lib/services/webauthn-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const webauthnService = new WebAuthnService(cookies.get('access_token'));
const userService = new UserService(cookies.get('access_token'));
const webauthnService = new WebAuthnService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const account = await userService.getCurrent();
const passkeys = await webauthnService.listCredentials();
return {

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AppConfigService from '$lib/services/app-config-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const appConfigService = new AppConfigService(cookies.get('access_token'));
const appConfigService = new AppConfigService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const appConfig = await appConfigService.list(true);
return { appConfig };
};

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OIDCService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const oidcService = new OIDCService(cookies.get('access_token'));
const oidcService = new OIDCService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const clients = await oidcService.listClients();
return clients;
};

View File

@@ -1,7 +1,8 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OidcService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => {
const oidcService = new OidcService(cookies.get('access_token'));
const oidcService = new OidcService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
return await oidcService.getClient(params.id);
};

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserGroupService from '$lib/services/user-group-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const userGroupService = new UserGroupService(cookies.get('access_token'));
const userGroupService = new UserGroupService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const userGroups = await userGroupService.list();
return userGroups;
};

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserGroupService from '$lib/services/user-group-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => {
const userGroupService = new UserGroupService(cookies.get('access_token'));
const userGroupService = new UserGroupService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const userGroup = await userGroupService.get(params.id);
return { userGroup };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const userService = new UserService(cookies.get('access_token'));
const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const users = await userService.list();
return users;
};

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => {
const userService = new UserService(cookies.get('access_token'));
const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const user = await userService.get(params.id);
return user;
};

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AuditLogService from '$lib/services/audit-log-service';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const auditLogService = new AuditLogService(cookies.get('access_token'));
const auditLogService = new AuditLogService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const auditLogs = await auditLogService.list({
sort: {
column: 'createdAt',

View File

@@ -2,9 +2,4 @@
reverse_proxy /api/* http://localhost:{$BACKEND_PORT:8080}
reverse_proxy /.well-known/* http://localhost:{$BACKEND_PORT:8080}
reverse_proxy /* http://localhost:{$PORT:3000}
log {
output file /var/log/caddy/access.log
level WARN
}
}

View File

@@ -8,9 +8,4 @@
reverse_proxy /* http://localhost:{$PORT:3000} {
trusted_proxies 0.0.0.0/0
}
log {
output file /var/log/caddy/access.log
level WARN
}
}

View File

@@ -1,3 +1,9 @@
# Check if the script is being run from the root of the project
if [ ! -f .version ] || [ ! -f frontend/package.json ] || [ ! -f CHANGELOG.md ]; then
echo "Error: This script must be run from the root of the project."
exit 1
fi
# Read the current version from .version
VERSION=$(cat .version)
@@ -29,6 +35,13 @@ else
exit 1
fi
# Confirm release creation
read -p "This will create a new $RELEASE_TYPE release with version $NEW_VERSION. Do you want to proceed? (y/n) " CONFIRM
if [[ "$CONFIRM" != "y" ]]; then
echo "Release process canceled."
exit 1
fi
# Update the .version file with the new version
echo $NEW_VERSION >.version
git add .version
@@ -58,4 +71,30 @@ git tag "v$NEW_VERSION"
git push
git push --tags
# Check if GitHub CLI is installed
if ! command -v gh &>/dev/null; then
echo "GitHub CLI (gh) is not installed. Please install it and authenticate using 'gh auth login'."
exit 1
fi
# Extract the changelog content for the latest release
echo "Extracting changelog content for version $NEW_VERSION..."
CHANGELOG=$(awk '/^## / {if (NR > 1) exit} NR > 1 {print}' CHANGELOG.md | awk 'NR > 2 || NF {print}')
if [ -z "$CHANGELOG" ]; then
echo "Error: Could not extract changelog for version $NEW_VERSION."
exit 1
fi
# Create the release on GitHub
echo "Creating GitHub release..."
gh release create "v$NEW_VERSION" --title "v$NEW_VERSION" --notes "$CHANGELOG"
if [ $? -eq 0 ]; then
echo "GitHub release created successfully."
else
echo "Error: Failed to create GitHub release."
exit 1
fi
echo "Release process complete. New version: $NEW_VERSION"