package tokens import ( "bytes" "context" "encoding/base64" "errors" "fmt" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "github.com/valyala/fasthttp" ) type fromHandler = func(h *fasthttp.RequestHeader) []byte const ( bearerTokenHdr = "Bearer" bearerTokenKey = "__context_bearer_token_key" ) // BearerToken usage: // // if err = storeBearerToken(ctx); err != nil { // log.Error("could not fetch bearer token", zap.Error(err)) // c.Error("could not fetch bearer token", fasthttp.StatusBadRequest) // return // } // BearerTokenFromHeader extracts a bearer token from Authorization request header. func BearerTokenFromHeader(h *fasthttp.RequestHeader) []byte { auth := h.Peek(fasthttp.HeaderAuthorization) if auth == nil || !bytes.HasPrefix(auth, []byte(bearerTokenHdr)) { return nil } if auth = bytes.TrimPrefix(auth, []byte(bearerTokenHdr+" ")); len(auth) == 0 { return nil } return auth } // BearerTokenFromCookie extracts a bearer token from cookies. func BearerTokenFromCookie(h *fasthttp.RequestHeader) []byte { auth := h.Cookie(bearerTokenHdr) if len(auth) == 0 { return nil } return auth } // StoreBearerToken extracts a bearer token from the header or cookie and stores // it in the request context. func StoreBearerToken(ctx *fasthttp.RequestCtx) error { tkn, err := fetchBearerToken(ctx) if err != nil { return err } // This is an analog of context.WithValue. ctx.SetUserValue(bearerTokenKey, tkn) return nil } // StoreBearerTokenAppCtx extracts a bearer token from the header or cookie and stores // it in the application context. func StoreBearerTokenAppCtx(ctx *fasthttp.RequestCtx, appCtx context.Context) (context.Context, error) { tkn, err := fetchBearerToken(ctx) if err != nil { return nil, err } newCtx := context.WithValue(appCtx, bearerTokenKey, tkn) return newCtx, nil } // LoadBearerToken returns a bearer token stored in the context given (if it's // present there). func LoadBearerToken(ctx context.Context) (*bearer.Token, error) { if tkn, ok := ctx.Value(bearerTokenKey).(*bearer.Token); ok && tkn != nil { return tkn, nil } return nil, errors.New("found empty bearer token") } func fetchBearerToken(ctx *fasthttp.RequestCtx) (*bearer.Token, error) { // ignore empty value if ctx == nil { return nil, nil } var ( lastErr error buf []byte tkn = new(bearer.Token) ) for _, parse := range []fromHandler{BearerTokenFromHeader, BearerTokenFromCookie} { if buf = parse(&ctx.Request.Header); buf == nil { continue } else if data, err := base64.StdEncoding.DecodeString(string(buf)); err != nil { lastErr = fmt.Errorf("can't base64-decode bearer token: %w", err) continue } else if err = tkn.Unmarshal(data); err != nil { lastErr = fmt.Errorf("can't unmarshal bearer token: %w", err) continue } return tkn, nil } return nil, lastErr }