Exemplo n.º 1
0
    def validate(user, encoded_token, kind):
        """
        Validate a token generated by TokenManager.generate(...).

        :param user: The user the token was generated for.
        :type user: authservice.models.User
        :param encoded_token: The token to verify.
        :type encoded_token: Base64 Encoded JWT Token
        :param kind: The expected type of the token
        :type kind: TokenType
        :return: JWT encoded claims or False
        :rtype: str or bool
        :Example:

        >>> user = User.objects.create(...)
        >>> token = TokenManager.generate(user, TokenType.LEVEL_ZERO)
        >>> TokenManager.validate(user, token, TokenType.LEVEL_ZERO)
        """
        assert(isinstance(user, User))
        assert(isinstance(kind, TokenType))
        assert(kind in list(TokenType))

        jwt = PyJWT(options={
            'require_exp': True,
            'require_iat': True,
        })

        try:
            claims = jwt.decode(
                encoded_token, user.private_key(), algorithms=['HS256'])
        except InvalidTokenError as e:
            log.warning(str(e))
            return False

        if ('sub' not in claims) or (claims['sub'] != user.uuid):
            log.debug("Missing SUB")
            return False

        if ('typ' not in claims) or (claims['typ'] is not int(kind.value)):
            log.debug("Missing TYP or invalid TYP")
            return False

        if 'jti' not in claims:
            log.debug("Missing JTI")
            return False

        # Is the jti in our database of valid jti?
        try:
            entry = ValidTokens.objects.get(jti=claims['jti'], user=user)
        except ValidTokens.DoesNotExist:
            return False
        else:
            seconds = int(entry.exp.timestamp())
            if seconds != claims['exp']:
                return False

        return claims
Exemplo n.º 2
0
 def __init__(self):
     self.pyjwt = PyJWT()
     self.header_prefix = 'JWT'
     self.token_ttl = timedelta(seconds=300)
     self.not_before = timedelta(seconds=0)
     self.algorithm = 'HS256'
     self.verify_claims = ['signature', 'exp', 'iat', 'nbf']
     self.require_claims = ['exp', 'iat', 'nbf']
     self.leeway = 0
     self.secret_key = None
    def test_decode_when_algorithm_not_available(self):
        token = jwt.encode(self.payload, PRIVATE_KEY,
                           algorithm='RS256').decode('utf-8')

        pyjwt_without_rsa = PyJWT()
        pyjwt_without_rsa.unregister_algorithm('RS256')
        with patch.object(jwt, 'decode', new=pyjwt_without_rsa.decode):
            with self.assertRaisesRegex(TokenBackendError,
                                        'Invalid algorithm specified'):
                self.rsa_token_backend.decode(token)
Exemplo n.º 4
0
    def __init__(self,
                 private_key: RSAPrivateKey = None,
                 public_key: RSAPublicKey = None,
                 expires=None,
                 nbf_delta=None,
                 algorithm="RS512"):

        self.__private_key = private_key
        self.__public_key = public_key
        self.__jwt = PyJWT(algorithms=self.ALGORITHMS)
        self.__expires = expires or self.DEFAULT_EXPIRATION
        self.__nbf_delta = nbf_delta or self.NBF_DELTA
        self.__algorithm = algorithm
Exemplo n.º 5
0
    def encode(self, payload, headers=None):
        """
        Returns an encoded token for the given payload dictionary.
        """
        jwt_payload = payload.copy()
        if self.audience is not None:
            jwt_payload['aud'] = self.audience
        if self.issuer is not None:
            jwt_payload['iss'] = self.issuer

        token = PyJWT().encode(jwt_payload,
                               self.signing_key,
                               algorithm=self.algorithm,
                               headers=headers)
        if isinstance(token, bytes):
            # For PyJWT <= 1.7.1
            return token.decode('utf-8')
        # For PyJWT >= 2.0.0a1
        return token
Exemplo n.º 6
0
def generateToken():
    headers = {
        "alg": "HS256",
        "typ": "JWT"
    }
    salt = "2095132720951327"
    exp = int(time.time())
    payload = {
        "userName": '******',
        "exp": exp
    }
    token = PyJWT().encode(payload=payload, key=salt,algorithm='HS256', headers=headers)
    return token
Exemplo n.º 7
0
    def generate(user, kind):
        """
        Generate a session token for the user, providing the level and exp.

        :param user: The user to generate the token for.
        :type user: models.User
        :param kind: The type of token to generate
        :type kind: models.TokenType
        :return: JWT encoded claims
        :rtype: str
        :Example:

        >>> user = User.objects.create(...)
        >>> token = TokenManager.generate(user, TokenType.LEVEL_ZERO)
        """
        assert(isinstance(kind, TokenType))
        assert(kind in list(TokenType))

        now = timezone.now()

        claims = {
            'iat': now,
            'exp': now + kind.ttl(),
            'sub': user.uuid,
            'jti': user.nonce(),
            'typ': int(kind.value),
        }

        jwt = PyJWT(options={
            'require_exp': True,
            'require_iat': True,
        })

        ValidTokens.objects.create(
            jti=claims['jti'], exp=claims['exp'], user=user)

        log.info("Generated a {} for {}".format(kind, user.uuid))
        return jwt.encode(claims, user.private_key(), algorithm='HS256')
Exemplo n.º 8
0
 def decode(self, token, verify=True):
     """
     Performs a validation of the given token and returns its payload
     dictionary.
     Raises a `TokenBackendError` if the token is malformed, if its
     signature check fails, or if its 'exp' claim indicates it has expired.
     """
     try:
         pj = PyJWT()
         payload = pj.decode(
             token,
             self.verifying_key,
             algorithms=[self.algorithm],
             verify=verify,
             audience=self.audience,
             issuer=self.issuer,
             options={'verify_aud': self.audience is not None})
         header = pj.get_unverified_header(token)
         return {'payload': payload, 'header': header}
     except InvalidAlgorithmError as ex:
         raise JWTInvalidError('Invalid algorithm specified') from ex
     except InvalidTokenError:
         raise JWTInvalidError('Token is invalid or expired')
Exemplo n.º 9
0
def login_user():
    email = request.json['email']
    password = request.json['password']

    user = User.query.filter_by(email=email).first_or_404()

    if not user.verify_password(password):
        return jsonify({"error": "Incorrect password"}), 403

    payload = {"id": user.id, "exp": datetime.utcnow() + timedelta(hours=12)}

    token = jwt.encode(self=None,
                       payload=payload,
                       key=current_app.config['AUTH_SECRET_KEY'])

    return jsonify({"token": token}), 200
Exemplo n.º 10
0
class Jwt(object):
    """ Base class implementing JWT support. """
    # For easier access
    Error = exc.JwtError

    AuthHeaderMissingError = exc.AuthHeaderMissingError
    ClaimMissing = exc.ClaimMissing
    BadAuthHeaderError = exc.BadAuthHeaderError
    InvalidTokenError = exc.InvalidTokenError
    NotAuthorizedError = exc.NotAuthorizedError
    UserNotFoundError = exc.UserNotFoundError
    TokenExpired = exc.TokenExpired

    def __init__(self):
        self.pyjwt = PyJWT()
        self.header_prefix = 'JWT'
        self.token_ttl = timedelta(seconds=300)
        self.not_before = timedelta(seconds=0)
        self.algorithm = 'HS256'
        self.verify_claims = ['signature', 'exp', 'iat', 'nbf']
        self.require_claims = ['exp', 'iat', 'nbf']
        self.leeway = 0
        self.secret_key = None

    def authorize(self, auth_header: Optional[str]) -> User:
        """ Given an Authorization Header try to get the matching user.

        Args:
            auth_header (Optional[str]):
                The full content of the 'Authorization' header as read from the
                request. The way it's stored in the request will depend on
                framework used.

        Returns:
            User: The user instance represented by the token read from *auth_header*.

        Raises:
            Jwt.AuthHeaderMissingError:
                If the given auth header is empty or `None`.
            Jwt.BadAuthHeaderError:
                If the given auth header cannot be parsed. This is either if
                the Authorization header is completely wrong or the header
                prefix does not match whatever is set in `Jwt.header_prefix`
            Jwt.InvalidTokenError:
                Cannot decode the JWT token.
            Jwt.UserNotFoundError:
                User represented by the token was not found. This might happen
                if the user is deleted after the token is issued but before it
                expires.
        """
        if not auth_header:
            raise self.AuthHeaderMissingError()

        token = self.get_token_from_header(auth_header)
        return self.authorize_token(token)

    def authorize_token(self, token: str) -> User:
        """ Get user for a given token.

        This method can be useful if the token is not coming from an HTTP header but
        other means (like websockets).

        Args:
            token:
                JWT token representing a user. This actually only stores the user
                ID, the actual user is retrievieved with `user_from_payload()` method.

        Returns:
            A user corresponding to the given token (if it's valid).

        Raises:
            Jwt.InvalidTokenError:
                If the token can't be decoded.
            Jwt.UserNotFoundError:
                If the user ID stored in the token cannot be found
                (.user_from_payload() call returned ``None``).
        """
        try:
            payload = self.decode_token(token)
        except PyJwtInvalidTokenError:
            raise self.InvalidTokenError(f"Failed to decode token '{token}'")

        user = self.user_from_payload(payload)
        if user is None:
            raise self.UserNotFoundError()

        return user

    def get_token_from_header(self, auth_header: str) -> str:
        """ Parse auth header and extract the token

        Args:
            auth_header:
                The content of the auth header as received with in the request.

        Returns:
            The JWT token stored in the header
        """
        # Verify the token is in the right format
        parts = auth_header.split()
        if parts[0] != self.header_prefix:
            raise self.BadAuthHeaderError(
                f"Bad auth header: '{parts[0]}', expected '{self.header_prefix}'"
            )
        elif len(parts) == 1:
            raise self.InvalidTokenError("Missing or empty token")

        return parts[1]

    def user_payload(self, user) -> JsonDict:
        """ Return payload for the given user.

        This method must be implemented by the subclasses in order to integrate
        with any storage used by the project (jwtlib itself is framework
        agnostic).
        """
        raise NotImplementedError("user_payload() method must be implemented")

    def user_from_payload(self, payload: JsonDict) -> User:
        """ Return a user for the given JWT payload.

        This method must be implemented by the subclasses in order to integrate
        with any storage used by the project (jwtlib itself is framework
        agnostic).

        This method is the opposite of `user_payload`.
        """
        raise NotImplementedError(
            "user_from_payload() method must be implemented")

    def generate_token(self, user: Optional[User]) -> str:
        """ Generate JWT token for the given user. """
        if user is None:
            raise self.NotAuthorizedError("No user to generate token for")

        headers = self.create_headers()
        payload = self.create_payload()
        payload.update(self.user_payload(user))

        missing = frozenset(self.require_claims) - frozenset(payload.keys())
        if missing:
            raise self.ClaimMissing("JWT payload is missing claims: {}".format(
                ', '.join(missing)))

        return self.pyjwt.encode(payload,
                                 self.secret_key,
                                 algorithm=self.algorithm,
                                 headers=headers)

    def create_headers(self) -> Optional[JsonDict]:
        """ Create general JWT token headers.

        This method can be overloaded in subclasses to customize the way tokens
        are generated.
        """
        return None

    def create_payload(self) -> JsonDict:
        """ Create core JWT payload.

        This will contain the fields that are required by JWT like expiration
        and will be included in every token generated.

        Be careful when you overload this method in subclasses as you will have
        to take care of including the necessary fields or the jwtlib will break.
        """
        iat = datetime.utcnow()

        return {
            'iat': iat,
            'exp': iat + self.token_ttl,
            'nbf': iat + self.not_before,
        }

    def decode_token(self, token: str) -> JsonDict:
        """ Decode the token and return it's payload. """
        opts = {'require_' + claim: True for claim in self.require_claims}
        opts.update({'verify_' + claim: True for claim in self.verify_claims})

        try:
            return self.pyjwt.decode(token,
                                     key=self.secret_key,
                                     options=opts,
                                     algorithms=[self.algorithm],
                                     leeway=self.leeway)
        except ExpiredSignatureError:
            raise self.TokenExpired()
Exemplo n.º 11
0
"""Extensions registry

All extensions here are used as singletons and
initialized in application factory
"""

from flask_bcrypt import Bcrypt
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from jwt import PyJWT

from skel.configurations.logger import log

db = SQLAlchemy()
migrate = Migrate()
bcrypt = Bcrypt()
cors = CORS(resources={r"/v1/*": {"origins": "*"}})
log.debug("in debug mode")
pwd_context = bcrypt
jwt = PyJWT()
Exemplo n.º 12
0
class JWT:
    __slots__ = ('__private_key', '__public_key', '__jwt', '__expires',
                 '__nbf_delta', '__algorithm')

    DEFAULT_EXPIRATION = 86400 * 30  # one month
    NBF_DELTA = 20
    ALGORITHMS = tuple({
        'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES521', 'ES512', 'PS256',
        'PS384', 'PS512'
    })

    def __init__(self,
                 private_key: RSAPrivateKey = None,
                 public_key: RSAPublicKey = None,
                 expires=None,
                 nbf_delta=None,
                 algorithm="RS512"):

        self.__private_key = private_key
        self.__public_key = public_key
        self.__jwt = PyJWT(algorithms=self.ALGORITHMS)
        self.__expires = expires or self.DEFAULT_EXPIRATION
        self.__nbf_delta = nbf_delta or self.NBF_DELTA
        self.__algorithm = algorithm

    def _date_to_timestamp(self, value, default, timedelta_func=add):
        if isinstance(value, timedelta):
            return timedelta_func(time.time(), value.total_seconds())
        elif isinstance(value, datetime):
            return value.timestamp()
        elif isinstance(value, (int, float)):
            return value
        elif value is Ellipsis:
            return default()

        raise ValueError(type(value))

    def encode(self,
               expired: DateType = ...,
               nbf: DateType = ...,
               **claims) -> str:
        if not self.__private_key:
            raise RuntimeError("Can't encode without private key")

        claims.update(
            dict(
                exp=int(
                    self._date_to_timestamp(
                        expired, lambda: time.time() + self.__expires)),
                nbf=int(
                    self._date_to_timestamp(
                        nbf,
                        lambda: time.time() - self.__nbf_delta,
                        timedelta_func=sub)),
            ))

        return self.__jwt.encode(
            claims,
            self.__private_key,
            algorithm=self.__algorithm,
        ).decode()

    def decode(self, token: str, verify=True, **kwargs) -> dict:
        if not self.__public_key:
            raise RuntimeError("Can't decode without public key")

        return self.__jwt.decode(token,
                                 key=self.__public_key,
                                 verify=verify,
                                 algorithms=self.ALGORITHMS,
                                 **kwargs)
Exemplo n.º 13
0
from flask import Flask, request
from flask_restful import Resource, Api
from jwt import PyJWT

from security import authenticate, identity

app = Flask(__name__)
app.secret_key = 'jose'
api = Api(app)

jwt = PyJWT(app, authenticate, identity)
items = []


class Item(Resource):
    def get(self, name):
        item = next(filter(lambda x: x['name'] == name, items), None)
        return {'item': item}, 200 if item else 404

    def post(self, name):
        if next(filter(lambda x: x['name'] == name, items), None):
            return {
                'message':
                "An item with name '{}' already exists.".format(name)
            }, 400

        data = request.get_json()
        item = {'name': data['name'], 'price': data['price']}
        items.append(item)
        return item, 201
Exemplo n.º 14
0
class JWT:
    __slots__ = (
        "__private_key", "__public_key", "__jwt",
        "__expires", "__nbf_delta", "__algorithm",
    )

    DEFAULT_EXPIRATION = 86400 * 30  # one month
    NBF_DELTA = 20
    ALGORITHMS = tuple({
        "RS256", "RS384", "RS512", "ES256", "ES384",
        "ES521", "ES512", "PS256", "PS384", "PS512",
    })

    def __init__(
        self,
        private_key: Optional[RSAPrivateKey] = None,
        public_key: Optional[RSAPublicKey] = None,
        expires: Optional[int] = None,
        nbf_delta: Optional[int] = None,
        algorithm: str = "RS512",
    ):

        self.__private_key = private_key
        self.__public_key = public_key
        self.__jwt = PyJWT(algorithms=self.ALGORITHMS)
        self.__expires = expires or self.DEFAULT_EXPIRATION
        self.__nbf_delta = nbf_delta or self.NBF_DELTA
        self.__algorithm = algorithm

    def _date_to_timestamp(
        self,
        value: DateType,
        default: Callable[[], R],
        timedelta_func: Callable[[float, float], int] = add,
    ) -> Union[int, float, R]:
        if isinstance(value, timedelta):
            return timedelta_func(time.time(), value.total_seconds())
        elif isinstance(value, datetime):
            return value.timestamp()
        elif isinstance(value, (int, float)):
            return value
        elif value is Ellipsis:
            return default()

        raise ValueError(type(value))

    def encode(
        self,
        expired: DateType = ...,
        nbf: DateType = ...,
        **claims: int
    ) -> str:
        if not self.__private_key:
            raise RuntimeError("Can't encode without private key")

        claims.update(
            dict(
                exp=int(
                    self._date_to_timestamp(
                        expired,
                        lambda: time.time() + self.__expires,
                    ),
                ),
                nbf=int(
                    self._date_to_timestamp(
                        nbf,
                        lambda: time.time() - self.__nbf_delta,
                        timedelta_func=sub,
                    ),
                ),
            ),
        )

        return self.__jwt.encode(
            claims,
            self.__private_key,
            algorithm=self.__algorithm,
        ).decode()

    def decode(
        self, token: str, verify: bool = True, **kwargs: Any
    ) -> Dict[str, Any]:
        if not self.__public_key:
            raise RuntimeError("Can't decode without public key")

        return self.__jwt.decode(
            token,
            key=self.__public_key,
            verify=verify,
            algorithms=self.ALGORITHMS,
            **kwargs,
        )