Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ For general configuration, [click here](docs_website/docs/configurations/general
- OAuth
- Google Cloud OAuth
- Okta OAuth
- GitHub OAuth
- LDAP

### Metastore
Expand Down
11 changes: 10 additions & 1 deletion docs_website/docs/integrations/add_auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ We will go through how to setup authentication with OAuth and LDAP starting from

### OAuth

Start by creating an OAuth client with the authentication provider (e.g. [Google](https://developers.google.com/identity/protocols/oauth2), [Okta](https://developer.okta.com/docs/guides/implement-oauth-for-okta/create-oauth-app/)). Make sure "http://localhost:10001/oauth2callback" is entered as allowed redirect uri. Once created, the next step is to change the querybook config by editing `containers/bundled_querybook_config.yaml`. Open that file and enter the following:
Start by creating an OAuth client with the authentication provider (e.g. [Google](https://developers.google.com/identity/protocols/oauth2), [Okta](https://developer.okta.com/docs/guides/implement-oauth-for-okta/create-oauth-app/), [GitHub](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)). Make sure "http://localhost:10001/oauth2callback" is entered as allowed redirect uri. Once created, the next step is to change the querybook config by editing `containers/bundled_querybook_config.yaml`. Open that file and enter the following:

#### Generic OAuth

Expand Down Expand Up @@ -54,6 +54,15 @@ OKTA_BASE_URL: https://[Redacted].okta.com/oauth2
PUBLIC_URL: http://localhost:10001
```

#### GitHub

```yaml
AUTH_BACKEND: app.auth.github_auth
PUBLIC_URL: http://localhost:10001
OAUTH_CLIENT_ID: '---Redacted---'
OAUTH_CLIENT_SECRET: '---Redacted---'
```

:::caution
DO NOT CHECK IN `OAUTH_CLIENT_SECRET` to the codebase. The example above is for testing only. For production, please use environment variables to provide this value.
:::
Expand Down
57 changes: 57 additions & 0 deletions querybook/server/app/auth/github_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import requests
from app.auth.oauth_auth import OAuthLoginManager, OAUTH_CALLBACK_PATH
from env import QuerybookSettings

from .utils import AuthenticationError

# Relevant github issue https://github.com/singingwolfboy/flask-dance/issues/235
os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1"


class GitHubLoginManager(OAuthLoginManager):
@property
def oauth_config(self):
return {
"callback_url": "{}{}".format(
QuerybookSettings.PUBLIC_URL, OAUTH_CALLBACK_PATH
),
"client_id": QuerybookSettings.OAUTH_CLIENT_ID,
"client_secret": QuerybookSettings.OAUTH_CLIENT_SECRET,
"authorization_url": "https://github.com/login/oauth/authorize",
"token_url": "https://github.com/login/oauth/access_token",
"profile_url": "https://api.github.com/user",
"scope": "user:email",
}

def _get_user_profile(self, access_token):
resp = requests.get(
self.oauth_config["profile_url"],
headers={"Authorization": "Bearer {}".format(access_token)},
)
if not resp or resp.status_code != 200:
raise AuthenticationError(
"Failed to fetch user profile, status ({0})".format(
resp.status if resp else "None"
)
)
return self._parse_user_profile(resp)

def _parse_user_profile(self, resp):
user = resp.json()

email = user["email"]
username = user["login"]
return username, email


login_manager = GitHubLoginManager()
ignore_paths = [OAUTH_CALLBACK_PATH]


def init_app(app):
login_manager.init_app(app)


def login(request):
return login_manager.login(request)
14 changes: 9 additions & 5 deletions querybook/server/app/auth/oauth_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ def __init__(self):

@property
def oauth_session(self):
oauth_config = self.oauth_config
return OAuth2Session(
self.oauth_config["client_id"],
scope=self.oauth_config["scope"],
redirect_uri=self.oauth_config["callback_url"],
oauth_config["client_id"],
scope=oauth_config["scope"],
redirect_uri=oauth_config["callback_url"],
)

@property
Expand Down Expand Up @@ -77,15 +78,15 @@ def oauth_callback(self):
return f"<h1>Error: {request.args.get('error')}</h1>"

code = request.args.get("code")

try:
access_token = self._fetch_access_token(code)
username, email = self._get_user_profile(access_token)
with DBSession() as session:
flask_login.login_user(
AuthUser(self.login_user(username, email, session=session))
)
except AuthenticationError:
except AuthenticationError as e:
LOG.error("Failed authenticate oauth user", e)
abort_unauthorized()

next_url = "/"
Expand Down Expand Up @@ -125,6 +126,9 @@ def _parse_user_profile(self, profile_response):

@with_session
def login_user(self, username, email, session=None):
if not username:
raise AuthenticationError("Username must not be empty!")

user = get_user_by_name(username, session=session)
if not user:
user = create_user(
Expand Down
4 changes: 4 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ function getDevServerSettings(env) {
target: QUERYBOOK_UPSTREAM,
changeOrigin: true,
},
'/oauth2callback': {
target: QUERYBOOK_UPSTREAM,
changeOrigin: true,
},
},
publicPath: '/build/',
onListening: (server) => {
Expand Down