from datetime import datetime, timezone from flask import g from jose import jwt import config from auth.auth0.helpers import sync_auth0_user, get_jwks from db import db from models.user import User from services.users_repository import UsersRepository from auth.auth0.constants import AUTH0_MCT_PERMISSION, AUTH0_ADMIN_ROLE from utils.exceptions import Forbidden, Unauthorized class Auth0AuthService: token = None users_repository = UsersRepository() def __init__(self, token): self.token = token def authorize(self): """User authorization""" payload = self.__get_payload() # payload = {"sub": "auth0|5cf0091d6c91d310fe6fcdc6"} self.__update_context(payload) def __get_rsa_key(self): """Get RSA key for encryption""" try: unverified_header = jwt.get_unverified_header(self.token) except jwt.JWTError: raise Unauthorized("Invalid token. Use an {} signed JWT Access Token.".format(config.AUTH0_ALGORITHM)) if unverified_header["alg"] != config.AUTH0_ALGORITHM: raise Unauthorized("Invalid token. Use an {} signed JWT Access Token.".format(config.AUTH0_ALGORITHM)) jwks = get_jwks() rsa_key = None for key in jwks["keys"]: if key["kid"] == unverified_header["kid"]: rsa_key = key break if not rsa_key: raise Unauthorized("Unable to find appropriate key.") return rsa_key def __get_payload(self): """Get user payload from jwt token""" try: payload = jwt.decode( self.token, self.__get_rsa_key(), algorithms=config.AUTH0_ALGORITHM, audience=config.AUTH0_API_AUDIENCE, issuer="https://" + config.AUTH0_LOGIN_DOMAIN + "/", ) except jwt.ExpiredSignatureError: raise Unauthorized("Token is expired.") except jwt.JWTClaimsError: raise Unauthorized("Incorrect claims, please check the audience and issuer.") except Exception: raise Unauthorized("Unable to parse authentication token.") if AUTH0_MCT_PERMISSION not in payload["permissions"]: raise Forbidden("Access to Decibel is denied.") return payload def __update_context(self, payload): """Update context with user info""" should_update = True g.user_id = None g.user_external_id = payload["sub"] g.is_admin = AUTH0_ADMIN_ROLE in payload[config.AUTH0_USER_METADATA_PATH]['roles'] g.user_permissions = payload["permissions"] user = self.users_repository.get_user_by_external_id(g.user_external_id) if user: g.user_id = user.id should_update = ( (user.updated_at + config.AUTH0_USER_UPDATE_INTERVAL_MIN <= datetime.now(timezone.utc)) or (g.is_admin != user.is_admin) ) if not g.user_id or should_update: user = sync_auth0_user(g.user_external_id) g.user_id = user.id db.session.query(User).filter_by(external_id=user.external_id).update( {User.email: user.email, User.is_admin: g.is_admin} ) db.session.commit() return True