def get_request_url(request: sanic.request.Request) -> str:
    url = request.url
    scheme = get_scheme(request)
    if not url.startswith(scheme + ":"):
        url = scheme + ":" + url.split(":", 1)[-1]

    return url
async def well_known_finger_handler(
        request: sanic.request.Request) -> sanic.response.BaseHTTPResponse:
    provider = get_provider(request)
    scheme = get_scheme(request)

    resource = request.args.get("resource")
    rel = request.args.get("rel")
    finger_url = request.app.url_for("well_known_finger_handler",
                                     _scheme=scheme,
                                     _external=True,
                                     _server=request.host)
    issuer = "{0}://{1}".format(scheme, request.host)

    logger.info("finger for resource: {0} rel: {1}".format(resource, rel))

    try:
        resp = provider.handle_finger(resource, rel, issuer, finger_url)
        return sanic.response.json(
            resp,
            content_type="application/jrd+json",
            headers={"Access-Control-Allow-Origin": "*"})
    except Exception as err:
        logger.exception("Caught error whilst handling finger url",
                         exc_info=err)

    return sanic.response.HTTPResponse(status=500)
 def get_callback_url(request: sanic.request):
     scheme = get_scheme(request)
     callback_url = list(
         urllib.parse.urlparse(request.app.url_for("handle_callback")))
     callback_url[0] = scheme
     callback_url[1] = request.host
     return urllib.parse.urlunparse(callback_url)
async def create_refresh_response_dic(
        request: sanic.request.Request, params: Dict[str,
                                                     Any]) -> Dict[str, Any]:
    provider = get_provider(request)
    # See https://tools.ietf.org/html/rfc6749#section-6

    scope_param = params["scope"]
    scope = scope_param if scope_param else params["token_obj"]["scope"]
    unauthorized_scopes = set(scope) - set(params["token_obj"]["scope"])
    if unauthorized_scopes:
        raise TokenError("invalid_scope")

    user = await provider.users.get_user_by_username(
        params["token_obj"]["user"])
    client = params["client"]

    token = provider.tokens.create_token(
        user=user,
        client=client,
        scope=scope,
        auth_time=params["token_obj"]["auth_time"],
        specific_claims=params["specific_claims"],
        expire_delta=provider.token_expire_time,
    )

    scheme = get_scheme(request)
    issuer = "{0}://{1}".format(scheme, request.host)

    # If the Token has an id_token it's an Authentication request.
    if params["token_obj"]["id_token"]:
        id_token_dic = provider.tokens.create_id_token(
            user=user,
            auth_time=token["auth_time"],
            issuer=issuer,
            client=client,
            nonce=None,
            expire_delta=provider.token_expire_time,
            at_hash=token["at_hash"],
            scope=token["scope"],
            specific_claims=token["specific_claims"],
        )
    else:
        id_token_dic = {}

    token["id_token"] = id_token_dic
    await provider.tokens.save_token(token)
    await provider.tokens.delete_token_by_access_token(
        params["token_obj"]["access_token"])

    id_token = await client.sign(id_token_dic, jwk_set=provider.jwk_set)

    dic = {
        "access_token": token["access_token"],
        "refresh_token": token["refresh_token"],
        "token_type": "bearer",
        "expires_in": provider.token_expire_time,
        "id_token": id_token,
    }

    return dic
async def create_code_response_dic(request: sanic.request.Request,
                                   params: Dict[str, Any]) -> Dict[str, Any]:
    provider = get_provider(request)
    # See https://tools.ietf.org/html/rfc6749#section-4.1

    user = await provider.users.get_user_by_username(params["code_obj"]["user"]
                                                     )
    client = params["client"]

    token = provider.tokens.create_token(
        user=user,
        client=client,
        auth_time=params["code_obj"]["auth_time"],
        scope=params["code_obj"]["scope"],
        expire_delta=provider.token_expire_time,
        specific_claims=params["code_obj"]["specific_claims"],
        code=params["code"],
    )

    scheme = get_scheme(request)
    issuer = "{0}://{1}".format(scheme, request.host)

    id_token_dic = provider.tokens.create_id_token(
        user=user,
        auth_time=token["auth_time"],
        client=client,
        issuer=issuer,
        expire_delta=provider.token_expire_time,
        nonce=params["code_obj"]["nonce"],
        at_hash=token["at_hash"],
        scope=token["scope"],
        specific_claims=token["specific_claims"],
    )

    token["id_token"] = id_token_dic
    await provider.tokens.save_token(token)
    await provider.codes.mark_used_by_id(params["code"])

    id_token = await client.sign(id_token_dic, jwk_set=provider.jwk_set)

    dic = {
        "access_token": token["access_token"],
        "refresh_token": token["refresh_token"],
        "token_type": "bearer",
        "expires_in": provider.token_expire_time,
        "id_token": id_token,
    }

    return dic
async def well_known_openid_config_handler(
        request: sanic.request.Request) -> sanic.response.BaseHTTPResponse:
    scheme = get_scheme(request)

    response = {
        "issuer":
        "{0}://{1}".format(scheme, request.host),
        "authorization_endpoint":
        request.app.url_for("authorize_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "token_endpoint":
        request.app.url_for("token_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "userinfo_endpoint":
        request.app.url_for("userinfo_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "jwks_uri":
        request.app.url_for("jwk_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "registration_endpoint":
        request.app.url_for("client_register_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "login_hint":
        "N/A",
        # TODO code_challenge_methods_supported
        "request_parameter_supported":
        True,
        "response_types_supported": [
            "code",
            "id_token",
            "id_token token",
            "code token",
            "code id_token",
            "code id_token token",
        ],
        "id_token_signing_alg_values_supported": ["HS256", "RS256", "ES256"],
        "subject_types_supported": ["public", "pairwise"],  # or pairwise
        "token_endpoint_auth_methods_supported": [
            "client_secret_post",
            "client_secret_basic",
            "private_key_jwt",
            "client_secret_jwt",
        ],
        "claims_supported": [
            "name",
            "family_name",
            "given_name",
            "middle_name",
            "nickname",
            "preferred_username",
            "profile",
            "picture",
            "website",
            "gender",
            "birthdate",
            "zoneinfo",
            "locale",
            "updated_at",
            "email",
            "email_verified",
            "address",
            "phone_number",
            "phone_number_verified",
        ],
        "grant_types_supported": [
            "authorization_code", "implicit", "refresh_token", "password",
            "client_credentials"
        ],
        "scopes_supported":
        list(SCOPES.keys()),
    }
    return sanic.response.json(response,
                               headers={"Access-Control-Allow-Origin": "*"})
async def client_register_handler(
        request: sanic.request.Request) -> sanic.response.BaseHTTPResponse:
    provider = get_provider(request)
    scheme = get_scheme(request)

    if "client_id" in request.args:
        # Client Read, check auth header
        try:
            token = request.headers["Authorization"].split("Bearer ")[-1]
            client = await provider.clients.get_client_by_access_token(token)
        except Exception:
            return sanic.response.text(
                body="",
                status=403,
                headers={"WWW-Authenticate": 'Bearer error="invalid_token"'})

        result = {
            "client_id": client.id,
            "client_secret": client.secret,
            "client_secret_expires_at": client.expires_at,
            "subject_type": client.type,
            "application_type": client.application_type,
            "response_types": client.response_types,
            "redirect_uris": client.callback_urls,
            "grant_types": client.grant_types,
            "contacts": client.contacts,
            "jwks_uri": client.jwks_url,
            "post_logout_redirect_uris": client.post_logout_redirect_urls,
            "request_uris": client.request_urls,
            # 'registration_client_uri': request.app.url_for('client_register_handler', _scheme=scheme,
            #                                                _external=True, _server=request.host,
            #                                                client_id=client_id),
            # 'registration_access_token': client.access_token,
        }
        if client.sector_identifier_uri:
            result["sector_identifier_uri"] = client.sector_identifier_uri
        if client.jwt_algo:
            result["id_token_signed_response_alg"] = client.jwt_algo
        if client.userinfo_signed_response_alg:
            result[
                "userinfo_signed_response_alg"] = client.userinfo_signed_response_alg

        status = 201

    else:
        if not provider.open_client_registration and not await provider.clients.auth_client_registration(
                request):
            return sanic.response.HTTPResponse(status=403)

        if not request.json or "redirect_uris" not in request.json:
            logger.warning("Did not provide any JSON or redirect_uris")
            result = {
                "error": "invalid_client_metadata",
                "error_description": "Invalid metadata"
            }
            return sanic.response.json(result, status=400)

        client_id = uuid.uuid4().hex[:16]
        client_name = request.json.get("client_name", client_id)
        client_secret = uuid.uuid4().hex
        client_secret_expires_at = 1577858400  # 1st jan 2020

        application_type = request.json.get("application_type")
        response_types = request.json.get("response_types",
                                          frozenset(["code"]))
        scopes = request.json.get("scope", ["openid"])
        redirect_uris = request.json.get("redirect_uris", [])
        grant_types = request.json.get("grant_types")
        contacts = request.json.get("contacts")
        jwks_uri = request.json.get("jwks_uri")
        jwks = request.json.get("jwks")
        post_logout_redirect_uris = request.json.get(
            "post_logout_redirect_uris")
        request_uris = request.json.get("request_uris")
        prompt = request.json.get("prompt",
                                  frozenset(["none", "login", "consent"]))
        sector_identifier_uri = request.json.get("sector_identifier_uri")
        subject_type = request.json.get("subject_type", "public")
        logo_uri = request.json.get("logo_uri")
        policy_uri = request.json.get("policy_uri")
        tos_uri = request.json.get("tos_uri")

        if isinstance(scopes, str):
            scopes = set(scopes.split())
        scopes.add("openid")
        # TODO request_object_signing_alg
        #

        require_consent = request.json.get("require_consent") is True
        reuse_consent = request.json.get("reuse_consent") is True
        id_token_signed_response_alg = request.json.get(
            "id_token_signed_response_alg")
        userinfo_signed_response_alg = request.json.get(
            "userinfo_signed_response_alg")
        userinfo_encrypted_response_alg = request.json.get(
            "userinfo_encrypted_response_alg")
        userinfo_encrypted_response_enc = request.json.get(
            "userinfo_encrypted_response_enc")

        for url in redirect_uris:
            if "#" in url:
                # NO BAD, shouldnt have fragments in url
                result = {
                    "error": "invalid_redirect_uri",
                    "error_description": "Bad redirect uri {0}".format(url)
                }
                return sanic.response.json(result, status=400)

        # Validate sector_identifier_uri, must contain a superset of redirect_uris
        if sector_identifier_uri:
            try:
                async with aiohttp.ClientSession() as session:
                    logger.info("Getting Sector Identifier URI {0}".format(
                        sector_identifier_uri))
                    async with session.get(sector_identifier_uri) as resp:
                        sector_json = await resp.json()
                        if not isinstance(sector_json, list):
                            raise Exception(
                                "sector identifier json is not a list")

                        invalid_uris = set(redirect_uris) - set(sector_json)
                        if invalid_uris:
                            raise Exception(
                                "Invalid redirect uris: {0}".format(
                                    invalid_uris))

            except Exception as err:
                logger.warning(
                    "Failed to get sector identifier uri: {0}".format(err))
                result = {
                    "error":
                    "invalid_client_metadata",
                    "error_description":
                    "Failed to validate sector identifier uri, {0}".format(
                        err),
                }
                return sanic.response.json(result, status=400)

        success, data = await provider.clients.add_client(
            id_=client_id,
            name=client_name,
            type_=subject_type,
            secret=client_secret,
            scopes=scopes,
            callback_urls=redirect_uris,
            require_consent=require_consent,
            reuse_consent=reuse_consent,
            response_types=response_types,
            application_type=application_type,
            contacts=contacts,
            expires_at=client_secret_expires_at,
            grant_types=grant_types,
            jwks_url=jwks_uri,
            jwt_algo=id_token_signed_response_alg,
            prompts=prompt,
            post_logout_redirect_urls=post_logout_redirect_uris,
            request_urls=request_uris,
            sector_identifier_uri=sector_identifier_uri,
            userinfo_signed_response_alg=userinfo_signed_response_alg,
            userinfo_encrypted_response_alg=userinfo_encrypted_response_alg,
            userinfo_encrypted_response_enc=userinfo_encrypted_response_enc,
            logo_uri=logo_uri,
            policy_uri=policy_uri,
            tos_uri=tos_uri,
            jwks=jwks,
        )

        if success:
            client: Client = data

            result = {
                "client_id":
                client_id,
                "client_secret":
                client_secret,
                "client_secret_expires_at":
                client_secret_expires_at,
                "subject_type":
                "confidential",
                "application_type":
                application_type,
                "response_types":
                response_types,
                "redirect_uris":
                redirect_uris,
                "grant_types":
                grant_types,
                "contacts":
                contacts,
                "jwks_uri":
                jwks_uri,
                "post_logout_redirect_uris":
                post_logout_redirect_uris,
                "request_uris":
                request_uris,
                "registration_client_uri":
                request.app.url_for("client_register_handler",
                                    _scheme=scheme,
                                    _external=True,
                                    _server=request.host,
                                    client_id=client_id),
                "registration_access_token":
                client.access_token,
                # 'token_endpoint_auth_method': 'client_secret_basic'
            }
            if sector_identifier_uri:
                result["sector_identifier_uri"] = sector_identifier_uri
            if id_token_signed_response_alg:
                result[
                    "id_token_signed_response_alg"] = id_token_signed_response_alg
            if logo_uri:
                result["logo_uri"] = logo_uri
            if tos_uri:
                result["tos_uri"] = tos_uri
            if policy_uri:
                result["policy_uri"] = policy_uri

            status = 201
        else:
            result = {
                "error": "invalid_client_metadata",
                "error_description": data
            }
            status = 500

    return sanic.response.json(result,
                               headers={
                                   "Cache-Control": "no-store",
                                   "Pragma": "no-cache"
                               },
                               status=status)
async def well_known_oauth_config_handler(
        request: sanic.request.Request) -> sanic.response.BaseHTTPResponse:
    scheme = get_scheme(request)

    response = {
        "issuer":
        "{0}://{1}".format(scheme, request.host),
        "authorization_endpoint":
        request.app.url_for("authorize_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "token_endpoint":
        request.app.url_for("token_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "jwks_uri":
        request.app.url_for("jwk_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "registration_endpoint":
        request.app.url_for("client_register_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "scopes_supported":
        list(SCOPES.keys()),
        "response_types_supported": [
            "code",
            "id_token",
            "id_token token",
            "code token",
            "code id_token",
            "code id_token token",
        ],
        "grant_types_supported": [
            "authorization_code", "implicit", "refresh_token", "password",
            "client_credentials"
        ],
        "token_endpoint_auth_methods_supported": [
            "client_secret_post",
            "client_secret_basic",
            "private_key_jwt",
            "client_secret_jwt",
            "none",
        ],
        "token_endpoint_auth_signing_alg_values_supported":
        ["HS256", "RS256", "ES256"],
        "introspection_endpoint":
        request.app.url_for("introspection_handler",
                            _scheme=scheme,
                            _external=True,
                            _server=request.host),
        "introspection_endpoint_auth_methods_supported": [
            "client_secret_post",
            "client_secret_basic",
            "private_key_jwt",
            "client_secret_jwt",
            "none",
        ],
        "code_challenge_methods_supported": ["S256"],
    }
    return sanic.response.json(response,
                               headers={"Access-Control-Allow-Origin": "*"})
async def create_authorize_response_params(
        request: sanic.request.Request, params: Dict[str, Any],
        user: Dict[str, Any]) -> Tuple[dict, dict]:
    provider = get_provider(request)
    client = params["client"]

    uri = urlsplit(params["redirect_uri"])
    query_params = parse_qs(uri.query)
    query_fragment = {}

    try:
        if params["grant_type"] in ("authorization_code", "hybrid"):
            code = await provider.codes.create_code(
                client=client,
                user=user,
                scopes=params["scopes"],
                code_expire=int(provider.code_expire_time),
                nonce=params["nonce"],
                code_challenge=params["code_challenge"],
                code_challenge_method=params["code_challenge_method"],
                specific_claims=params["specific_claims"],
            )

        if params["grant_type"] == "authorization_code":
            # noinspection PyUnboundLocalVariable
            query_params["code"] = code["code"]
            query_params["state"] = params["state"]

        elif params["grant_type"] in ["implicit", "hybrid"]:
            token = provider.tokens.create_token(
                user=user,
                client=client,
                auth_time=user["auth_time"],
                scope=params["scopes"],
                expire_delta=provider.token_expire_time,
                specific_claims=params["specific_claims"],
            )

            # Check if response_type must include access_token in the response.
            if params["response_type"] in ("id_token token", "token",
                                           "code token",
                                           "code id_token token"):
                query_fragment["access_token"] = token["access_token"]

            # We don't need id_token if it's an OAuth2 request.
            if "openid" in params["scopes"]:
                scheme = get_scheme(request)
                issuer = "{0}://{1}".format(scheme, request.host)

                kwargs = {
                    "auth_time": token["auth_time"],
                    "user": user,
                    "client": client,
                    "issuer": issuer,
                    "expire_delta": provider.token_expire_time,
                    "nonce": params["nonce"],
                    "at_hash": token["at_hash"],
                    "scope": params["scopes"],
                    "specific_claims": params["specific_claims"],
                }
                # Include at_hash when access_token is being returned.
                if "access_token" in query_fragment:
                    kwargs["at_hash"] = token["at_hash"]
                id_token_dic = provider.tokens.create_id_token(**kwargs)

                # Check if response_type must include id_token in the response.
                if params["response_type"] in ("id_token", "id_token token",
                                               "code id_token",
                                               "code id_token token"):

                    query_fragment["id_token"] = await client.sign(
                        id_token_dic, jwk_set=provider.jwk_set)
            else:
                id_token_dic = {}

            # Store the token.
            token["id_token"] = id_token_dic
            await provider.tokens.save_token(token)

            # Code parameter must be present if it's Hybrid Flow.
            if params["grant_type"] == "hybrid":
                # noinspection PyUnboundLocalVariable
                query_fragment["code"] = code["code"]

            query_fragment["token_type"] = "bearer"
            query_fragment["expires_in"] = provider.token_expire_time
            query_fragment["state"] = params["state"]

    except Exception as err:
        logger.exception("Failed whilst creating authorize response",
                         exc_info=err)
        raise AuthorizeError(params["redirect_uri"], "server_error",
                             params["grant_type"])

    query_params = {key: value for key, value in query_params.items() if value}
    query_fragment = {
        key: value
        for key, value in query_fragment.items() if value
    }

    return query_params, query_fragment
            async def decorated_function(
                    request: sanic.request, *args,
                    **kwargs) -> sanic.response.BaseHTTPResponse:
                if "user" in request.ctx.session:
                    if request.ctx.session["user"][
                            "expires_at"] > datetime.datetime.now().timestamp(
                            ):
                        response = await f(request, *args, **kwargs)
                        return response

                    # Try refreshing access token to avoid a redirect
                    if request.ctx.session["user"]["refresh_token"]:
                        try:
                            await self.post_token_endpoint(
                                request=request,
                                refresh_token=request.ctx.session["user"]
                                ["refresh_token"])
                            logger.debug("Successfully refreshed tokens")
                        except Exception:
                            pass  # We've tried, we encountered an error, redirect the user
                        else:
                            # Success, now process the response
                            response = await f(request, *args, **kwargs)
                            return response

                    del request.ctx.session[
                        "user"]  # User has expired, remove data

                current_url = list(urllib.parse.urlparse(request.url))
                current_url[0] = get_scheme(request)
                current_url = urllib.parse.urlunparse(current_url)

                state = str(uuid.uuid4())
                nonce = str(uuid.uuid4())
                request.ctx.session["oicp_state"] = state
                request.ctx.session["oicp_redirect"] = current_url
                request.ctx.session["oicp_nonce"] = nonce

                params = {
                    "scope": self.string_scopes,
                    "response_type": "code",
                    "client_id": self.id,
                    "redirect_uri": self.get_callback_url(request),
                    "state": state,
                    "nonce": nonce,
                }

                if not self.authorize_url:
                    if self.autodiscover_url:
                        success = await self.autodiscover_settings()
                        if not success:
                            return sanic.response.text(
                                "SSO client library failed to autodiscover settings"
                            )
                    else:
                        # Settings not passed during setup.
                        return sanic.response.text(
                            "SSO client library not setup correctly, authorize_url not provided"
                        )

                redirect_url = list(urllib.parse.urlparse(self.authorize_url))
                redirect_url[4] = urllib.parse.urlencode(params)
                redirect_url = urllib.parse.urlunparse(redirect_url)

                return redirect(redirect_url)
async def validate_token_params(
        request: sanic.request.Request) -> Dict[str, Any]:
    provider = get_provider(request)

    if request.method == "POST":
        req_dict = request.form
    else:
        req_dict = request.args

    client_assertion_type = req_dict.get("client_assertion_type")
    client_assertion = req_dict.get("client_assertion")

    if client_assertion_type and client_assertion_type == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer":
        header = jwt.get_unverified_header(client_assertion)
        audience = "{0}://{1}{2}".format(get_scheme(request), request.host,
                                         request.path)

        # TODO maintain central collection of client jwts
        if "kid" in header:
            # Asymetric signing
            temp_jwt_token = jwt.decode(client_assertion, verify=False)
            client = await provider.clients.get_client_by_id(
                temp_jwt_token["sub"])
            jwt_key = client.jwk.get_key(header.get("kid"))

            try:
                jwt.decode(client_assertion,
                           jwt_key.export_to_pem(),
                           algorithms=[header["alg"]],
                           audience=audience)
                # By it not erroring, its successfully verified
            except Exception as err:
                logger.exception("Invalid key id", exc_info=err)
                raise TokenError("invalid_client")

        else:
            # HMAC with client secret
            temp_jwt_token = jwt.decode(client_assertion, verify=False)
            client = await provider.clients.get_client_by_id(
                temp_jwt_token["sub"])

            try:
                jwt.decode(client_assertion,
                           client.secret,
                           algorithms=[header["alg"]],
                           audience=audience)
                # By it not erroring, its successfully verified
            except Exception as err:
                logger.exception("Invalid key id", exc_info=err)
                raise TokenError("invalid_client")

    else:
        client_id = req_dict.get("client_id")
        client_secret = req_dict.get("client_secret", "")
        if "authorization" in request.headers and not client_id:
            hdr = request.headers["authorization"]
            if "Basic" in hdr:
                client_id, client_secret = base64.b64decode(
                    hdr.split()[-1].encode()).decode().split(":")
            else:
                raise NotImplementedError(hdr)

        client = await provider.clients.get_client_by_id(client_id)
        if not client_id:
            raise TokenError("invalid_client")

    if not client:
        raise TokenError("invalid_client")

    specific_claims = req_dict.get("claims")
    if specific_claims:
        try:
            specific_claims = json.loads(specific_claims)
        except Exception as err:
            logger.exception("Failed to decode specific claims", exc_info=err)

    result = {
        "client": client,
        "grant_type": req_dict.get("grant_type", ""),
        "code": req_dict.get("code", ""),
        "state": req_dict.get("state", ""),
        "scope": req_dict.get("scope", ""),
        "redirect_uri": req_dict.get("redirect_uri", ""),
        "refresh_token": req_dict.get("refresh_token", ""),
        "code_verifier": req_dict.get("code_verifier"),
        "username": req_dict.get("username", ""),
        "password": req_dict.get("password", ""),
        "specific_claims": specific_claims,
    }

    # if client.type == 'confidential' and client_secret != client.secret:
    #     raise TokenError('invalid_client')

    if result["grant_type"] == "authorization_code":
        if result["redirect_uri"] not in client.callback_urls:
            raise TokenError("invalid_client")

        code = await provider.codes.get_by_id(result["code"])

        if not code:
            raise TokenError("invalid_grant")

        if code["used"]:
            await provider.tokens.delete_token_by_code(result["code"])
            raise TokenError("invalid_grant")

        if code["client"] != client.id:
            raise TokenError("invalid_grant")

        if result["code_verifier"]:
            if code["code_challenge_method"] == "S256":
                new_code_challenge = (base64.urlsafe_b64encode(
                    hashlib.sha256(result["code_verifier"].encode(
                        "ascii")).digest()).decode("utf-8").replace("=", ""))
            else:
                new_code_challenge = result["code_verifier"]

            if new_code_challenge != code["code_challenge"]:
                raise TokenError("invalid_grant")

        result["code_obj"] = code

    elif result["grant_type"] == "password":
        if not provider.allow_grant_type_password:
            raise TokenError("unsupported_grant_type")

        # TODO authenticate username/password
        # result['username'] result['password']
        user = False

        if not user:
            raise UserAuthError()

        result["user_obj"] = user

    elif result["grant_type"] == "client_credentials":
        # TODO not sure about this
        raise NotImplementedError()

    elif result["grant_type"] == "refresh_token":
        if not result["refresh_token"]:
            logger.warning("No refresh token")
            raise TokenError("invalid_grant")

        token = await provider.tokens.get_token_by_refresh_token(
            result["refresh_token"])
        if not token:
            raise TokenError("invalid_grant")

        result["token_obj"] = token

    return result