Skip to content
Snippets Groups Projects
revocationEndpoint.go 3.67 KiB
Newer Older
package revocation

import (
	"encoding/json"

	"github.com/gofiber/fiber/v2"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
Gabriel Zachmann's avatar
Gabriel Zachmann committed
	"github.com/oidc-mytoken/api/v0"
	"github.com/oidc-mytoken/server/internal/config"
	"github.com/oidc-mytoken/server/internal/db"
	"github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/transfercoderepo"
	"github.com/oidc-mytoken/server/internal/model"
	"github.com/oidc-mytoken/server/internal/utils/errorfmt"
Gabriel Zachmann's avatar
Gabriel Zachmann committed
	sharedModel "github.com/oidc-mytoken/server/shared/model"
	"github.com/oidc-mytoken/server/shared/mytoken"
	mytokenPkg "github.com/oidc-mytoken/server/shared/mytoken/pkg"
	"github.com/oidc-mytoken/server/shared/utils"
// HandleRevoke handles requests to the revocation endpoint
func HandleRevoke(ctx *fiber.Ctx) error {
	log.Debug("Handle revocation request")
Gabriel Zachmann's avatar
Gabriel Zachmann committed
	req := api.RevocationRequest{}
	if err := json.Unmarshal(ctx.Body(), &req); err != nil {
		return model.ErrorToBadRequestErrorResponse(err).Send(ctx)
	}
	log.Trace("Parsed mytoken request")
	if req.Token == "" {
		req.Token = ctx.Cookies("mytoken")
	errRes := revokeAnyToken(nil, req.Token, req.OIDCIssuer, req.Recursive)
	if errRes != nil {
		return errRes.Send(ctx)
	}
	if clearCookie {
		return model.Response{
			Status: fiber.StatusNoContent,
			Cookies: []*fiber.Cookie{{
				Name:     "mytoken",
				Value:    "",
				Path:     "/api",
				Expires:  time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
				Secure:   config.Get().Server.Secure,
				HTTPOnly: true,
				SameSite: "Strict",
			}},
		}.Send(ctx)
	}
	return ctx.SendStatus(fiber.StatusNoContent)
}

func revokeAnyToken(tx *sqlx.Tx, token, issuer string, recursive bool) (errRes *model.Response) {
	if utils.IsJWT(token) { // normal Mytoken
		return revokeMytoken(tx, token, issuer, recursive)
	} else if len(token) == config.Get().Features.Polling.Len { // Transfer Code
		return revokeTransferCode(tx, token, issuer)
	} else { // Short Token
		shortToken := transfercoderepo.ParseShortToken(token)
		var valid bool
		if err := db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error {
			jwt, v, err := shortToken.JWT(tx)
			valid = v
			if err != nil {
				return err
			}
			token = jwt
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			return shortToken.Delete(tx)
		}); err != nil {
			log.Errorf("%s", errorfmt.Full(err))
			return model.ErrorToInternalServerErrorResponse(err)
		return revokeMytoken(tx, token, issuer, recursive)
func revokeMytoken(tx *sqlx.Tx, jwt, issuer string, recursive bool) (errRes *model.Response) {
	mt, err := mytokenPkg.ParseJWT(jwt)
	if err != nil {
	if issuer != "" && mt.OIDCIssuer != issuer {
		return &model.Response{
			Status:   fiber.StatusBadRequest,
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			Response: sharedModel.BadRequestError("token not for specified issuer"),
	return mytoken.RevokeMytoken(tx, mt.ID, jwt, recursive, mt.OIDCIssuer)
func revokeTransferCode(tx *sqlx.Tx, token, issuer string) (errRes *model.Response) {
	transferCode := transfercoderepo.ParseTransferCode(token)
	err := db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error {
		revokeMT, err := transferCode.GetRevokeJWT(tx)
		if err != nil {
			return err
		}
			jwt, valid, err := transferCode.JWT(tx)
			if err != nil {
				return err
			}
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			if valid { // if !valid the jwt field could not decrypted correctly, so we can skip that, but still delete
				// the TransferCode
				errRes = revokeAnyToken(tx, jwt, issuer, true)
				if errRes != nil {
					return errors.New("placeholder")
				}
			}
		}
		return transferCode.Delete(tx)
	})
	if err != nil && errRes == nil {
		log.Errorf("%s", errorfmt.Full(err))
		return model.ErrorToInternalServerErrorResponse(err)