Skip to content
Snippets Groups Projects
revocationEndpoint.go 3.72 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,
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			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",
				},
			},
	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
Gabriel Zachmann's avatar
Gabriel Zachmann committed
		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
				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)
Gabriel Zachmann's avatar
Gabriel Zachmann committed
	err := db.RunWithinTransaction(
		tx, func(tx *sqlx.Tx) error {
			revokeMT, err := transferCode.GetRevokeJWT(tx)
			if err != nil {
				return err
			}
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			if revokeMT {
				jwt, valid, err := transferCode.JWT(tx)
				if err != nil {
					return err
				}
				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")
					}
Gabriel Zachmann's avatar
Gabriel Zachmann committed
			return transferCode.Delete(tx)
		},
	)
	if err != nil && errRes == nil {
		log.Errorf("%s", errorfmt.Full(err))
		return model.ErrorToInternalServerErrorResponse(err)