示例#1
0
def test_valid_id_token_without_nonce(app):
    """
    Create a token and then validate it and make sure there are no exceptions
    when a nonce is not provided.
    """
    issuer = config.get("BASE_URL")
    keypair = app.keypairs[0]
    client_id = "client_12345"
    user = User(username="******", is_admin=False)
    expires_in = 2592000
    nonce = None
    max_age = None
    token_result = generate_signed_id_token(
        keypair.kid,
        keypair.private_key,
        user,
        expires_in,
        client_id,
        audiences=[client_id],
        auth_time=None,
        max_age=None,
        nonce=None,
    )
    unsigned_token = UnsignedIDToken.from_signed_and_encoded_token(
        token_result.token,
        client_id=client_id,
        issuer=issuer,
        max_age=max_age,
        nonce=nonce,
    )
    unsigned_token.validate()
    assert not unsigned_token.get("nonce")
示例#2
0
def test_valid_id_token(app):
    """
    Create a token and then validate it and make sure there are no exceptions
    """
    issuer = config.get("BASE_URL")
    keypair = app.keypairs[0]
    client_id = "client_12345"
    user = User(username="******", is_admin=False)
    expires_in = 2592000
    nonce = "a1b2c3d4e5f6g7h8i9j0k!l@#n$%^q&*stuvwxyz"
    max_age = None
    token_result = generate_signed_id_token(
        keypair.kid,
        keypair.private_key,
        user,
        expires_in,
        client_id,
        audiences=[client_id],
        auth_time=None,
        max_age=None,
        nonce=None,
    )
    unsigned_token = UnsignedIDToken.from_signed_and_encoded_token(
        token_result.token,
        client_id=client_id,
        issuer=issuer,
        max_age=max_age,
        nonce=nonce,
    )
    unsigned_token.validate()
示例#3
0
def test_id_token_max_age(app):
    """
    Create a token and then validate it and make sure there are no exceptions
    when a nonce is not provided.

    FIXME: We should test that this tries to re-auth user, not throw exception
    """
    keypair = app.keypairs[0]
    client_id = "client_12345"
    user = User(username='******', is_admin=False)
    expires_in = 2592000
    nonce = None
    max_age = 1
    now = int(time.time()) - 10

    with pytest.raises(IDTokenError):
        generate_signed_id_token(
            keypair.kid, keypair.private_key, user, expires_in, client_id,
            audiences=[client_id], auth_time=now, max_age=max_age, nonce=nonce)
示例#4
0
def test_recode_id_token(app, kid, rsa_private_key):
    """
    Test that after signing, unsigning, re-signing, and unsigning again,
    the contents of the ID Token that should be the same, are.
    """
    issuer = config.get("BASE_URL")
    keypair = app.keypairs[0]
    client_id = "client_12345"
    user = User(username="******", is_admin=False)
    expires_in = 2592000
    nonce = "a1b2c3d4e5f6g7h8i9j0k!l@#n$%^q&*stuvwxyz"
    max_age = None

    original_signed_token = generate_signed_id_token(
        keypair.kid,
        keypair.private_key,
        user,
        expires_in,
        client_id,
        audiences=[client_id],
        auth_time=None,
        max_age=max_age,
        nonce=nonce,
    )
    original_unsigned_token = UnsignedCodeIDToken.from_signed_and_encoded_token(
        original_signed_token.token,
        client_id=client_id,
        issuer=issuer,
        max_age=max_age,
        nonce=nonce,
    )

    new_signed_token = original_unsigned_token.get_signed_and_encoded_token(
        kid, rsa_private_key)
    new_unsigned_token = UnsignedCodeIDToken.from_signed_and_encoded_token(
        new_signed_token,
        client_id=client_id,
        issuer=issuer,
        max_age=max_age,
        nonce=nonce,
    )

    assert original_unsigned_token.iss == new_unsigned_token.iss
    assert original_unsigned_token.sub == new_unsigned_token.sub
    assert original_unsigned_token.aud == new_unsigned_token.aud
    assert original_unsigned_token.azp == new_unsigned_token.azp
    assert original_unsigned_token.nonce == new_unsigned_token.nonce
示例#5
0
def test_expired_id_token(app):
    """
    Create a token that is already expired make sure an exception is thrown.
    """
    keypair = app.keypairs[0]
    client_id = "client_12345"
    user = User(username='******', is_admin=False)
    expires_in = 0
    nonce = None
    max_age = None

    with pytest.raises(IDTokenError):
        token = generate_signed_id_token(
            keypair.kid, keypair.private_key, user, expires_in, client_id,
            audiences=[client_id], auth_time=None, max_age=max_age, nonce=nonce
        )
        assert not token
示例#6
0
def create_id_token(user,
                    keypair,
                    expires_in,
                    client_id,
                    audiences=None,
                    auth_time=None,
                    max_age=None,
                    nonce=None):
    try:
        return token.generate_signed_id_token(keypair.kid,
                                              keypair.private_key,
                                              user,
                                              expires_in,
                                              client_id,
                                              audiences=audiences,
                                              auth_time=auth_time,
                                              max_age=max_age,
                                              nonce=nonce)
    except Exception as e:
        return flask.jsonify({'errors': e.message})
示例#7
0
    def __call__(self,
                 client,
                 grant_type,
                 expires_in=None,
                 scope=None,
                 include_refresh_token=True,
                 nonce=None,
                 refresh_token=None,
                 refresh_token_claims=None):
        """
        Generate the token response, which looks like the following:

            {
                'token_type': 'Bearer',
                'id_token': 'eyJhb[...long encoded JWT...]OnoVQ',
                'access_token': 'eyJhb[...long encoded JWT...]evfxA',
                'refresh_token': 'eyJhb[ ... long encoded JWT ... ]KnLJA',
                'expires_in': 1200,
            }

        This function will be called in authlib internals.

        Args:
            client: not used (would be used to determine expiration)
            grant_type: not used
            expires_in: not used (see expiration times configured above)
            scope (List[str]): list of requested scopes
            include_refresh_token: not used
            nonce (str): "nonsense" to include in ID token (see OIDC spec)
            refresh_token:
                for a refresh token grant, pass in the previous refresh token
                to return that same token again instead of generating a new one
                (otherwise this will let the refresh token refresh itself)
            refresh_token_claims (dict):
                also for a refresh token grant, pass the previous refresh token
                claims (to avoid having to encode or decode the refresh token
                here)
        """
        # Find the ``User`` model.
        # The way to do this depends on the grant type.
        if grant_type == 'authorization_code':
            # For authorization code grant, get the code from either the query
            # string or the form data, and use that to look up the user.
            if flask.request.method == 'GET':
                code = flask.request.args.get('code')
            else:
                code = flask.request.form.get('code')
            user = (current_session.query(AuthorizationCode).filter_by(
                code=code).first().user)
        if grant_type == 'refresh_token':
            # For refresh token, the user ID is the ``sub`` field in the token.
            user = (current_session.query(User).filter_by(
                id=int(refresh_token_claims['sub'])).first())

        keypair = flask.current_app.keypairs[0]
        id_token = generate_signed_id_token(
            kid=keypair.kid,
            private_key=keypair.private_key,
            user=user,
            expires_in=self.ACCESS_TOKEN_EXPIRES_IN,
            client_id=client.client_id,
            audiences=scope,
            nonce=nonce,
        )
        access_token = generate_signed_access_token(
            kid=keypair.kid,
            private_key=keypair.private_key,
            user=user,
            expires_in=self.ACCESS_TOKEN_EXPIRES_IN,
            scopes=scope,
        )
        # If ``refresh_token`` was passed (for instance from the refresh
        # grant), use that instead of generating a new one.
        if refresh_token is None:
            refresh_token, _ = generate_signed_refresh_token(
                kid=keypair.kid,
                private_key=keypair.private_key,
                user=user,
                expires_in=self.REFRESH_TOKEN_EXPIRES_IN,
                scopes=scope,
            )
        # ``expires_in`` is just the access token expiration time.
        expires_in = self.ACCESS_TOKEN_EXPIRES_IN
        return {
            'token_type': 'Bearer',
            'id_token': id_token,
            'access_token': access_token,
            'refresh_token': refresh_token,
            'expires_in': expires_in,
        }
示例#8
0
def generate_implicit_response(client,
                               grant_type,
                               include_access_token=True,
                               expires_in=None,
                               user=None,
                               scope=None,
                               nonce=None,
                               **kwargs):
    # prevent those bothersome "not bound to session" errors
    if user not in current_session:
        user = current_session.query(User).filter_by(id=user.id).first()

    if not user:
        raise OIDCError("user not authenticated")

    keypair = flask.current_app.keypairs[0]

    linked_google_email = get_linked_google_account_email(user.id)
    linked_google_account_exp = get_linked_google_account_exp(user.id)

    if not isinstance(scope, list):
        scope = scope.split(" ")

    if not "user" in scope:
        scope.append("user")

    # ``expires_in`` is just the token expiration time.
    expires_in = config["ACCESS_TOKEN_EXPIRES_IN"]

    response = {
        "token_type": "Bearer",
        "expires_in": expires_in,
        # "state" handled in authlib
    }

    # don't provide user projects access in access_tokens for implicit flow
    # due to issues with "Location" header size during redirect (and b/c
    # of general deprecation of user access information in tokens)
    if include_access_token:
        access_token = generate_signed_access_token(
            kid=keypair.kid,
            private_key=keypair.private_key,
            user=user,
            expires_in=config["ACCESS_TOKEN_EXPIRES_IN"],
            scopes=scope,
            client_id=client.client_id,
            linked_google_email=linked_google_email,
            include_project_access=False,
        ).token
        response["access_token"] = access_token

    # don't provide user projects access in id_tokens for implicit flow
    # due to issues with "Location" header size during redirect (and b/c
    # of general deprecation of user access information in tokens)
    id_token = generate_signed_id_token(
        kid=keypair.kid,
        private_key=keypair.private_key,
        user=user,
        expires_in=config["ACCESS_TOKEN_EXPIRES_IN"],
        client_id=client.client_id,
        audiences=scope,
        nonce=nonce,
        linked_google_email=linked_google_email,
        linked_google_account_exp=linked_google_account_exp,
        include_project_access=False,
        auth_flow_type=AuthFlowTypes.IMPLICIT,
        access_token=access_token if include_access_token else None,
    ).token
    response["id_token"] = id_token

    return response
示例#9
0
def generate_token_response(client,
                            grant_type,
                            expires_in=None,
                            user=None,
                            scope=None,
                            include_refresh_token=True,
                            nonce=None,
                            refresh_token=None,
                            refresh_token_claims=None,
                            **kwargs):
    # prevent those bothersome "not bound to session" errors
    if user not in current_session:
        user = current_session.query(User).filter_by(id=user.id).first()

    if not user:
        # Find the ``User`` model.
        # The way to do this depends on the grant type.
        if grant_type == "authorization_code":
            # For authorization code grant, get the code from either the query
            # string or the form data, and use that to look up the user.
            if flask.request.method == "GET":
                code = flask.request.args.get("code")
            else:
                code = flask.request.form.get("code")
            user = (current_session.query(AuthorizationCode).filter_by(
                code=code).first().user)
        if grant_type == "refresh_token":
            # For refresh token, the user ID is the ``sub`` field in the token.
            user = (current_session.query(User).filter_by(
                id=int(refresh_token_claims["sub"])).first())

    keypair = flask.current_app.keypairs[0]

    linked_google_email = get_linked_google_account_email(user.id)
    linked_google_account_exp = get_linked_google_account_exp(user.id)

    if not isinstance(scope, list):
        scope = scope.split(" ")

    access_token = generate_signed_access_token(
        kid=keypair.kid,
        private_key=keypair.private_key,
        user=user,
        expires_in=config["ACCESS_TOKEN_EXPIRES_IN"],
        scopes=scope,
        client_id=client.client_id,
        linked_google_email=linked_google_email,
    ).token
    id_token = generate_signed_id_token(
        kid=keypair.kid,
        private_key=keypair.private_key,
        user=user,
        expires_in=config["ACCESS_TOKEN_EXPIRES_IN"],
        client_id=client.client_id,
        audiences=scope,
        nonce=nonce,
        linked_google_email=linked_google_email,
        linked_google_account_exp=linked_google_account_exp,
        auth_flow_type=AuthFlowTypes.CODE,
        access_token=access_token,
    ).token
    # If ``refresh_token`` was passed (for instance from the refresh
    # grant), use that instead of generating a new one.
    if refresh_token is None:
        refresh_token = generate_signed_refresh_token(
            kid=keypair.kid,
            private_key=keypair.private_key,
            user=user,
            expires_in=config["REFRESH_TOKEN_EXPIRES_IN"],
            scopes=scope,
            client_id=client.client_id,
        ).token
    # ``expires_in`` is just the access token expiration time.
    expires_in = config["ACCESS_TOKEN_EXPIRES_IN"]
    return {
        "token_type": "Bearer",
        "id_token": id_token,
        "access_token": access_token,
        "refresh_token": refresh_token,
        "expires_in": expires_in,
    }
示例#10
0
def generate_implicit_response(client,
                               grant_type,
                               include_access_token=True,
                               expires_in=None,
                               user=None,
                               scope=None,
                               nonce=None,
                               **kwargs):
    # prevent those bothersome "not bound to session" errors
    if user not in current_session:
        user = current_session.query(User).filter_by(id=user.id).first()

    if not user:
        raise OIDCError("user not authenticated")

    keypair = flask.current_app.keypairs[0]

    linked_google_email = get_linked_google_account_email(user.id)
    linked_google_account_exp = get_linked_google_account_exp(user.id)

    if not isinstance(scope, list):
        scope = scope.split(" ")

    if not "user" in scope:
        scope.append("user")

    id_token = generate_signed_id_token(
        kid=keypair.kid,
        private_key=keypair.private_key,
        user=user,
        expires_in=ACCESS_TOKEN_EXPIRES_IN,
        client_id=client.client_id,
        audiences=scope,
        nonce=nonce,
        linked_google_email=linked_google_email,
        linked_google_account_exp=linked_google_account_exp,
    ).token

    # ``expires_in`` is just the token expiration time.
    expires_in = ACCESS_TOKEN_EXPIRES_IN

    response = {
        "token_type": "Bearer",
        "id_token": id_token,
        "expires_in": expires_in,
        # "state" handled in authlib
    }

    if include_access_token:
        access_token = generate_signed_access_token(
            kid=keypair.kid,
            private_key=keypair.private_key,
            user=user,
            expires_in=ACCESS_TOKEN_EXPIRES_IN,
            scopes=scope,
            client_id=client.client_id,
            linked_google_email=linked_google_email,
        ).token
        response["access_token"] = access_token

    return response