- шаг схемы закрывает поверхность, которую хранилище приносит открытой: собственную регистрацию, вход по паролю и одноразовый код — без этого закрытие приёма обходилось двумя запросами - продление сессии выключено, срок семь суток: иначе отзыв доступа у провайдера до сервиса не доходит никогда - файл записи отдаётся вошедшему по токену файла — пересмотр ADR-2026-08-12-file-link-open-but-not-logged
73 lines
3.3 KiB
Go
73 lines
3.3 KiB
Go
package http
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/pocketbase/pocketbase/apis"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"github.com/pocketbase/pocketbase/tools/hook"
|
|
)
|
|
|
|
// RefreshPath — адрес хранилища, которым сессия продлевает саму себя.
|
|
const RefreshPath = "/api/collections/users/auth-refresh"
|
|
|
|
// BlockSessionRefresh закрывает продление сессии.
|
|
//
|
|
// Хранилище выдаёт сессию продлеваемой: предъявитель значения меняет его на
|
|
// новое, с новым сроком, и делает это сколько угодно раз, никуда не входя. При
|
|
// живом продлении срок жизни сессии перестаёт что-либо значить, а вместе с ним
|
|
// перестаёт работать единственный канал, которым отзыв доступа у провайдера
|
|
// доходит до сервиса, — сервис после входа к провайдеру не обращается.
|
|
//
|
|
// Решение владельца от 2026-08-12: продление выключено, цена — вход раз в
|
|
// семь суток.
|
|
func BlockSessionRefresh() *hook.Handler[*core.RequestEvent] {
|
|
return &hook.Handler[*core.RequestEvent]{
|
|
Id: "transcriberBlockSessionRefresh",
|
|
Priority: apis.DefaultLoadAuthTokenMiddlewarePriority - 2,
|
|
Func: func(e *core.RequestEvent) error {
|
|
if e.Request.URL.Path == RefreshPath {
|
|
return e.JSON(http.StatusNotFound, map[string]string{
|
|
"error": "Продление сессии выключено",
|
|
})
|
|
}
|
|
|
|
return e.Next()
|
|
},
|
|
}
|
|
}
|
|
|
|
// SessionFromCookie перекладывает значение куки сессии в заголовок, которым
|
|
// хранилище читает предъявленную сессию.
|
|
//
|
|
// Куки хранилище не читает вовсе — только заголовок `Authorization`. Браузер же
|
|
// сам заголовка не шлёт, а своей страницы со скриптом у сервиса нет, поэтому
|
|
// сессия предъявляется кукой, а способ проверки остаётся один.
|
|
//
|
|
// Предъявленный заголовок побеждает: иначе браузер с сессионной кукой получал
|
|
// бы на собственных адресах хранилища не то, что предъявил.
|
|
//
|
|
// Слой стоит раньше проверки токена: тот идёт с приоритетом
|
|
// DefaultLoadAuthTokenMiddlewarePriority и к этому моменту заголовок должен
|
|
// быть на месте.
|
|
func SessionFromCookie() *hook.Handler[*core.RequestEvent] {
|
|
return &hook.Handler[*core.RequestEvent]{
|
|
Id: "transcriberSessionFromCookie",
|
|
Priority: apis.DefaultLoadAuthTokenMiddlewarePriority - 1,
|
|
Func: func(e *core.RequestEvent) error {
|
|
if e.Request.Header.Get("Authorization") != "" {
|
|
return e.Next()
|
|
}
|
|
|
|
cookie, err := e.Request.Cookie(SessionCookieName)
|
|
if err != nil || cookie.Value == "" {
|
|
return e.Next()
|
|
}
|
|
|
|
e.Request.Header.Set("Authorization", cookie.Value)
|
|
|
|
return e.Next()
|
|
},
|
|
}
|
|
}
|