Skip to content
Snippets Groups Projects
Commit 1bd91852 authored by Gabriel Zachmann's avatar Gabriel Zachmann
Browse files

implement creating transfer code from existing ST

parent 104487fd
No related branches found
No related tags found
No related merge requests found
Showing
with 175 additions and 20 deletions
......@@ -55,10 +55,12 @@ features:
# Support for short super tokens
short_tokens:
enabled: true
len: 64 # Default 64, max 256
# Support for transfer codes for super tokens
transfer_codes:
enabled: true
len: 8 # Default 8, max 64
# Support for polling codes that are used by native applications. Only disable this if you have good reasons for it.
polling_codes:
......
......@@ -47,8 +47,15 @@ var defaultConfig = Config{
model.OIDCFlowAuthorizationCode,
},
TokenRevocation: onlyEnable{true},
ShortTokens: onlyEnable{true},
TransferCodes: onlyEnable{true},
ShortTokens: shortTokenConfig{
Enabled: true,
Len: 64,
},
TransferCodes: transferCodeConfig{
Enabled: true,
Len: 8,
ExpiresAfter: 300,
},
Polling: pollingConf{
Enabled: true,
PollingCodeExpiresAfter: 300,
......@@ -74,13 +81,24 @@ type Config struct {
}
type featuresConf struct {
EnabledOIDCFlows []model.OIDCFlow `yaml:"enabled_oidc_flows"`
TokenRevocation onlyEnable `yaml:"token_revocation"`
ShortTokens onlyEnable `yaml:"short_tokens"`
TransferCodes onlyEnable `yaml:"transfer_codes"`
Polling pollingConf `yaml:"polling_codes"`
AccessTokenGrant onlyEnable `yaml:"access_token_grant"`
SignedJWTGrant onlyEnable `yaml:"signed_jwt_grant"`
EnabledOIDCFlows []model.OIDCFlow `yaml:"enabled_oidc_flows"`
TokenRevocation onlyEnable `yaml:"token_revocation"`
ShortTokens shortTokenConfig `yaml:"short_tokens"`
TransferCodes transferCodeConfig `yaml:"transfer_codes"`
Polling pollingConf `yaml:"polling_codes"`
AccessTokenGrant onlyEnable `yaml:"access_token_grant"`
SignedJWTGrant onlyEnable `yaml:"signed_jwt_grant"`
}
type shortTokenConfig struct {
Enabled bool `yaml:"enabled"`
Len int `yaml:"len"`
}
type transferCodeConfig struct {
Enabled bool `yaml:"enabled"`
Len int `yaml:"len"`
ExpiresAfter int `yaml:"expires_after"`
}
type onlyEnable struct {
......
......@@ -39,7 +39,7 @@ func HandleAccessTokenEndpoint(ctx *fiber.Ctx) error {
}
log.Trace("Checked grant type")
revoked, dbErr := dbUtils.CheckTokenRevoked(req.SuperToken)
token, revoked, dbErr := dbUtils.CheckTokenRevoked(req.SuperToken)
if dbErr != nil {
return model.ErrorToInternalServerErrorResponse(dbErr).Send(ctx)
}
......@@ -51,6 +51,7 @@ func HandleAccessTokenEndpoint(ctx *fiber.Ctx) error {
return res.Send(ctx)
}
log.Trace("Checked token not revoked")
req.SuperToken = token
st, err := supertoken.ParseJWT(req.SuperToken)
if err != nil {
......
package pkg
type CreateTransferCodeRequest struct {
SuperToken string `json:"super_token"`
}
package pkg
import (
"github.com/zachmann/mytoken/internal/model"
)
type TransferCodeResponse struct {
SuperTokenType model.ResponseType `json:"super_token_type"`
TransferCode string `json:"transfer_code"`
ExpiresIn uint64 `json:"expires_in"`
}
package super
import (
"encoding/json"
uuid "github.com/satori/go.uuid"
"github.com/jmoiron/sqlx"
"github.com/zachmann/mytoken/internal/supertoken/event"
event2 "github.com/zachmann/mytoken/internal/supertoken/event/pkg"
"github.com/zachmann/mytoken/internal/utils/dbUtils"
"github.com/zachmann/mytoken/internal/config"
"github.com/zachmann/mytoken/internal/utils"
"github.com/zachmann/mytoken/internal/db"
"github.com/gofiber/fiber/v2"
"github.com/zachmann/mytoken/internal/endpoints/token/super/pkg"
"github.com/zachmann/mytoken/internal/model"
"github.com/zachmann/mytoken/internal/utils/ctxUtils"
)
func HandleCreateTransferCodeForExistingSuperToken(ctx *fiber.Ctx) error {
token := ctxUtils.GetAuthHeaderToken(ctx)
if len(token) == 0 {
var req pkg.CreateTransferCodeRequest
if err := json.Unmarshal(ctx.Body(), &req); err != nil {
res := &model.Response{
Status: fiber.StatusBadRequest,
Response: model.BadRequestError(err.Error()),
}
return res.Send(ctx)
}
token = req.SuperToken
if len(token) == 0 {
res := &model.Response{
Status: fiber.StatusUnauthorized,
Response: model.BadRequestError("required parameter 'super_token' missing"),
}
return res.Send(ctx)
}
}
superToken, revoked, dbErr := dbUtils.CheckTokenRevoked(token)
if dbErr != nil {
return model.ErrorToInternalServerErrorResponse(dbErr).Send(ctx)
}
if revoked {
res := &model.Response{
Status: fiber.StatusUnauthorized,
Response: model.InvalidTokenError("token not valid"),
}
return res.Send(ctx)
}
transferCode := utils.RandASCIIString(config.Get().Features.TransferCodes.Len)
expiresIn := config.Get().Features.TransferCodes.ExpiresAfter
var stid uuid.UUID
if err := db.Transact(func(tx *sqlx.Tx) error {
if err := tx.Get(&stid, `SELECT id FROM SuperTokens WHERE token=?`, superToken); err != nil {
return err
}
if _, err := tx.Exec(`INSERT INTO TransferCodes (transfer_code, ST_id, expires_in, new_st) VALUES(?,?,?,0)`, transferCode, stid, expiresIn); err != nil {
return err
}
return nil
}); err != nil {
return model.ErrorToInternalServerErrorResponse(err).Send(ctx)
}
if err := event.LogEvent(&event2.Event{
Type: event2.STEventTransferCodeCreated,
Comment: "from existing ST",
}, stid, *ctxUtils.NetworkData(ctx)); err != nil {
return model.ErrorToInternalServerErrorResponse(err).Send(ctx)
}
res := &model.Response{
Status: fiber.StatusOK,
Response: pkg.TransferCodeResponse{
SuperTokenType: model.ResponseTypeTransferCode,
TransferCode: transferCode,
ExpiresIn: uint64(expiresIn),
},
}
return res.Send(ctx)
}
......@@ -68,7 +68,7 @@ func (g *GrantType) UnmarshalJSON(data []byte) error {
return nil
}
func (g *GrantType) MarshalJSON() ([]byte, error) {
func (g GrantType) MarshalJSON() ([]byte, error) {
return json.Marshal(g.String())
}
......
......@@ -64,7 +64,7 @@ func (f *OIDCFlow) UnmarshalJSON(data []byte) error {
return nil
}
func (f *OIDCFlow) MarshalJSON() ([]byte, error) {
func (f OIDCFlow) MarshalJSON() ([]byte, error) {
return json.Marshal(f.String())
}
......
......@@ -65,7 +65,7 @@ func (r *ResponseType) UnmarshalJSON(data []byte) error {
return nil
}
func (r *ResponseType) MarshalJSON() ([]byte, error) {
func (r ResponseType) MarshalJSON() ([]byte, error) {
return json.Marshal(r.String())
}
......
......@@ -21,4 +21,7 @@ func addAPIv0Routes(s fiber.Router) {
if config.Get().Features.TokenRevocation.Enabled {
tokens.Post("/revoke", revocation.HandleRevoke)
}
if config.Get().Features.TransferCodes.Enabled {
tokens.Post("/transfer", super.HandleCreateTransferCodeForExistingSuperToken)
}
}
......@@ -62,7 +62,7 @@ func eventStringToInt(str string) int {
}
// AllEvents hold all possible Events
var allEvents = [...]string{"unknown", "AT_created", "ST_created", "tokeninfo_history", "tokeninfo_tree", "tokeninfo_list_super_tokens", "mng_enabled_AT_grant", "mng_disabled_AT_grant", "mng_enabled_JWT_grant", "mng_disabled_JWT_grant", "mng_linked_grant", "mng_unlinked_grant", "mng_enabled_tracing", "mng_disabled_tracing", "inherited_RT"}
var allEvents = [...]string{"unknown", "AT_created", "ST_created", "tokeninfo_history", "tokeninfo_tree", "tokeninfo_list_super_tokens", "mng_enabled_AT_grant", "mng_disabled_AT_grant", "mng_enabled_JWT_grant", "mng_disabled_JWT_grant", "mng_linked_grant", "mng_unlinked_grant", "mng_enabled_tracing", "mng_disabled_tracing", "inherited_RT", "transfer_code_created", "transfer_code_used"}
// Events for SuperTokens
const (
......@@ -81,5 +81,7 @@ const (
STEventMngTracingEnabled
STEventMngTracingDisabled
STEventInheritedRT
STEventTransferCodeCreated
STEventTransferCodeUsed
maxEvent
)
......@@ -38,7 +38,7 @@ func HandleSuperTokenFromSuperToken(ctx *fiber.Ctx) *model.Response {
// GrantType already checked
revoked, dbErr := dbUtils.CheckTokenRevoked(req.SuperToken)
token, revoked, dbErr := dbUtils.CheckTokenRevoked(req.SuperToken)
if dbErr != nil {
return model.ErrorToInternalServerErrorResponse(dbErr)
}
......@@ -49,6 +49,7 @@ func HandleSuperTokenFromSuperToken(ctx *fiber.Ctx) *model.Response {
}
}
log.Trace("Checked token not revoked")
req.SuperToken = token
st, err := supertoken.ParseJWT(req.SuperToken)
if err != nil {
......
package ctxUtils
import (
"strings"
"github.com/gofiber/fiber/v2"
)
func GetAuthHeaderToken(ctx *fiber.Ctx) (token string) {
authHeader := string(ctx.Request().Header.Peek("Authorization"))
if strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
token = authHeader[7:]
}
return
}
......@@ -73,13 +73,22 @@ FROM childs
return err
}
func CheckTokenRevoked(token string) (bool, error) {
// CheckTokenRevoked takes a short super token or a normal super token and checks if it was revoked. If the token is found in the db, the super token string will be returned.
// Therefore, this function can also be used to exchange a short super token into a normal one.
func CheckTokenRevoked(token string) (string, bool, error) {
var count int
if err := db.DB().Get(&count, `SELECT COUNT(1) FROM SuperTokens WHERE token=?`, token); err != nil {
return true, err
return token, true, err
}
if count == 0 {
return true, nil
if count > 0 { // token was found as SuperToken
return token, false, nil
}
return false, nil
var superToken string
if err := db.DB().Get(&superToken, `SELECT token FROM ShortSuperTokensV WHERE short_token=?`, token); err != nil {
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return token, true, err
}
return superToken, true, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment