github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/identity/token.go (about)

     1  // Copyright 2023 Northern.tech AS
     2  //
     3  //	Licensed under the Apache License, Version 2.0 (the "License");
     4  //	you may not use this file except in compliance with the License.
     5  //	You may obtain a copy of the License at
     6  //
     7  //	    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //	Unless required by applicable law or agreed to in writing, software
    10  //	distributed under the License is distributed on an "AS IS" BASIS,
    11  //	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //	See the License for the specific language governing permissions and
    13  //	limitations under the License.
    14  package identity
    15  
    16  import (
    17  	"encoding/base64"
    18  	"encoding/json"
    19  	"net/http"
    20  	"strings"
    21  
    22  	"github.com/mendersoftware/go-lib-micro/addons"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  type Identity struct {
    27  	Subject  string         `json:"sub" valid:"required"`
    28  	Tenant   string         `json:"mender.tenant,omitempty"`
    29  	IsUser   bool           `json:"mender.user,omitempty"`
    30  	IsDevice bool           `json:"mender.device,omitempty"`
    31  	Plan     string         `json:"mender.plan,omitempty"`
    32  	Addons   []addons.Addon `json:"mender.addons,omitempty"`
    33  	Trial    bool           `json:"mender.trial"`
    34  }
    35  
    36  // ExtractJWTFromHeader inspect the Authorization header for a Bearer token and
    37  // if not present looks for a "JWT" cookie.
    38  func ExtractJWTFromHeader(r *http.Request) (jwt string, err error) {
    39  	auth := r.Header.Get("Authorization")
    40  	if auth == "" {
    41  		jwtCookie, err := r.Cookie("JWT")
    42  		if err != nil {
    43  			return "", errors.New("Authorization not present in header")
    44  		}
    45  		jwt = jwtCookie.Value
    46  	} else {
    47  		auths := strings.Split(auth, " ")
    48  
    49  		if len(auths) != 2 {
    50  			return "", errors.Errorf("malformed Authorization header")
    51  		}
    52  
    53  		if !strings.EqualFold(auths[0], "Bearer") {
    54  			return "", errors.Errorf("unknown Authorization method %s", auths[0])
    55  		}
    56  		jwt = auths[1]
    57  	}
    58  	return jwt, nil
    59  }
    60  
    61  // Generate identity information from given JWT by extracting subject and tenant claims.
    62  // Note that this function does not perform any form of token signature
    63  // verification.
    64  func ExtractIdentity(token string) (id Identity, err error) {
    65  	var (
    66  		claims []byte
    67  		jwt    []string
    68  	)
    69  	jwt = strings.Split(token, ".")
    70  	if len(jwt) != 3 {
    71  		return id, errors.New("identity: incorrect token format")
    72  	}
    73  	claims, err = base64.RawURLEncoding.DecodeString(jwt[1])
    74  	if err != nil {
    75  		return id, errors.Wrap(err,
    76  			"identity: failed to decode base64 JWT claims")
    77  	}
    78  	err = json.Unmarshal(claims, &id)
    79  	if err != nil {
    80  		return id, errors.Wrap(err,
    81  			"identity: failed to decode JSON JWT claims")
    82  	}
    83  	return id, id.Validate()
    84  }
    85  
    86  func (id Identity) Validate() error {
    87  	if id.Subject == "" {
    88  		return errors.New("identity: claim \"sub\" is required")
    89  	}
    90  	return nil
    91  }