mher / flower

Real-time monitor and web admin for Celery distributed task queue
https://flower.readthedocs.io
Other
6.47k stars 1.09k forks source link

Flower API with Authorization Code Grant Flow #987

Open Fan-Gong opened 4 years ago

Fan-Gong commented 4 years ago

I know Flower supports the following authentication types:

But none of them satisfies the need for our application, therefore we supported a custom authentication type for integrating with our own application' Auth service using OAuth2.0

Basically we mimic the file in flower/views/auth.py and created a customAuthhandler which is working quite good -- user will be redirected to our login page if they are not authorized to access the flower UI.

And now I want to use flower API as well, in that case what should I do to make the API be authorized? I have tried to use flower client secret and ID to generate bearer token for flower API but seems not working. (I am not quite familiar with OAuth), so is there a easy solution (like client credentials grant type) to access the flower API when it is using Authorization Code Grant type?

Thanks!

ljluestc commented 1 year ago
  1. Generate OAuth Tokens for API Access:

    The Authorization Code Grant type requires you to go through the authorization process to obtain access tokens. Since you've implemented custom authentication, you'll need to mimic this process to generate tokens for API access.

  2. API Requests with Bearer Token:

    Once you have an access token, you can use it as a bearer token in the Authorization header of your API requests. This will authenticate your requests and allow you to access Flower's API endpoints.

Here's a simplified example of how you might do this using Python's requests library:

import requests

# Replace with your actual OAuth2 endpoint and credentials
token_url = "https://your-auth-service/token"
client_id = "your-client-id"
client_secret = "your-client-secret"

# Mimic the OAuth2 authorization process to get the access token
# This might involve sending a POST request to the token_url with client_id, client_secret, grant_type, etc.
# Extract the access token from the response
access_token = "your-access-token"

# Make API requests using the access token as a bearer token
api_url = "http://flower-api-url/api-endpoint"
headers = {"Authorization": f"Bearer {access_token}"}

response = requests.get(api_url, headers=headers)
print(response.json())

Please note that the exact implementation might vary based on how your custom authentication system is set up and the requirements of your OAuth2 flow. You'll need to adapt the example to fit your specific situation.

As for providing complete updated and fixed code, it's not feasible to provide a one-size-fits-all solution without detailed knowledge of your application's architecture, your custom authentication service, and Flower's setup. If you encounter specific issues or error messages, I'd be happy to help you troubleshoot and provide guidance accordingly.

notmmao commented 7 months ago

Here is my solution. This is translated with GPT, hoping there is no ambiguity.

This solution involves a custom LoginHandler, which in the example inherits from flower.views.auth.GitLabLoginHandler.

Users can log in using the web method without any changes. However, when accessing the API directly, for example, using requests, authorization can be achieved by including Authorization: Bearer <access_token> in the header.

The <access_token> can be obtained from your.gitlab.domain by generating a personal access token with the read_api permission.

It is important to note that to avoid frequent calls to GitLab's OAuth, it is strongly recommended to use request.session to store the authorized cookie.

import os

import tornado.web
from flower.views.auth import GitLabLoginHandler

class MyAuthHandler(GitLabLoginHandler):
    _OAUTH_GITLAB_DOMAIN = os.getenv(
        "FLOWER_GITLAB_OAUTH_DOMAIN", "your.gitlab.domian")
    _OAUTH_AUTHORIZE_URL = f'https://{_OAUTH_GITLAB_DOMAIN}/oauth/authorize'
    _OAUTH_ACCESS_TOKEN_URL = f'https://{_OAUTH_GITLAB_DOMAIN}/oauth/token'
    _OAUTH_NO_CALLBACKS = False

    async def get(self):
        auth_header = self.request.headers.get("Authorization", "")

        if not auth_header:
            return await super().get()
        else:
            bearer, token = auth_header.split(' ')
            user = {'access_token': token}
            return await self._on_auth(user)