    async def _get_oauth_client_allowed_scopes(self):
        """Private method: get oauth allowed scopes

        Handle:

        - oauth_client_allowed_scopes
        - callable config
        - deprecated oauth_roles config
        - access_scopes
        """
        # cases:
        # 1. only scopes
        # 2. only roles
        # 3. both! (conflict, favor scopes)
        scopes = []
        if self.oauth_client_allowed_scopes:
            allowed_scopes = self.oauth_client_allowed_scopes
            if callable(allowed_scopes):
                allowed_scopes = allowed_scopes(self)
                if inspect.isawaitable(allowed_scopes):
                    allowed_scopes = await allowed_scopes
            scopes.extend(allowed_scopes)

        if self.oauth_roles:
            if scopes:
                # both defined! Warn
                warnings.warn(
                    f"Ignoring deprecated Spawner.oauth_roles={self.oauth_roles} in favor of Spawner.oauth_client_allowed_scopes.",
                )
            else:
                role_names = self.oauth_roles
                if callable(role_names):
                    role_names = role_names(self)
                roles = list(
                    self.db.query(orm.Role).filter(orm.Role.name.in_(role_names))
                )
                if len(roles) != len(role_names):
                    missing_roles = set(role_names).difference(
                        {role.name for role in roles}
                    )
                    raise ValueError(f"No such role(s): {', '.join(missing_roles)}")
                scopes.extend(roles_to_scopes(roles))

        # always add access scope
        scopes.append(f"access:servers!server={self.user.name}/{self.name}")
        return sorted(set(scopes))
Private method: get oauth allowed scopes

Handle:

- oauth_client_allowed_scopes
- callable config
- deprecated oauth_roles config
- access_scopes
jupyterhub/spawner.py