vikunja/pkg/routes/api/v2/auth_refresh.go

76 lines
2.6 KiB
Go

// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-present Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package apiv2
import (
"context"
"net/http"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/modules/auth"
user2 "code.vikunja.io/api/pkg/user"
"github.com/danielgtaylor/huma/v2"
)
func init() { AddRouteRegistrar(RegisterRefreshTokenRoutes) }
// RegisterRefreshTokenRoutes wires the refresh-token endpoint. It authenticates
// via the HttpOnly refresh cookie rather than a JWT, so it is a public operation.
func RegisterRefreshTokenRoutes(api huma.API) {
Register(api, huma.Operation{
OperationID: "auth-refresh-token",
Summary: "Refresh user token",
Description: "Exchanges the refresh-token cookie for a new short-lived JWT. The refresh token is rotated on every call, so the previous one stops working. A new HttpOnly refresh cookie is set on the response.",
Method: http.MethodPost,
Path: "/user/token/refresh",
DefaultStatus: http.StatusOK,
Tags: []string{"auth"},
Security: publicSecurity,
}, authRefreshToken)
}
func authRefreshToken(ctx context.Context, _ *struct{}) (*authTokenBody, error) {
ec := echoContextFromCtx(ctx)
if ec == nil {
return nil, huma.Error401Unauthorized("No refresh token provided.")
}
cookie, err := ec.Cookie(auth.RefreshTokenCookieName)
if err != nil || cookie.Value == "" {
return nil, huma.Error401Unauthorized("No refresh token provided.")
}
result, err := auth.RefreshSession(cookie.Value)
if err != nil {
if user2.IsErrUserStatusError(err) {
auth.ClearRefreshTokenCookie(ec)
}
return nil, translateDomainError(err)
}
cookieMaxAge := int(config.ServiceJWTTTL.GetInt64())
if result.IsLongSession {
cookieMaxAge = int(config.ServiceJWTTTLLong.GetInt64())
}
auth.SetRefreshTokenCookie(ec, result.NewRefreshToken, cookieMaxAge)
out := &authTokenBody{CacheControl: "no-store"}
out.Body.Token = result.AccessToken
return out, nil
}