Skip to content

Commit

Permalink
Auth rbac (#136)
Browse files Browse the repository at this point in the history
* show datatype along with column name

* rename pjt-dev -> dev

* revert accidental commit of local port change

* SVGChart axis settings in folder. Improve error display in SettingsDialogComponent

* add (hard-coded) fraction tooltip to DotPlot

* slightly fancier tooltip

* remove unused local variables

* tooltip moves with pointer

* StackedRowChart tooltip

* add blockquote stying for use in TextBoxChart

* change fraction to percentage in tooltip

* change vite config, custom onLog handling seems less relevant now

* fix issue with some environments not serving project json files

* Revert "rename pjt-dev -> dev"

This reverts commit bef69a2.

* fix home button route and don't save

* adjust position of delete icon

* minor tweaks to types, show chart json options in non-dev

* fix bug with ColumnSelection and columns added via link, add filter support

* disable allow_user_add for ReactWordCloudChart

* lint add missing dependency

* fix ChartType.required signature & add `allow_user_add`

* first proto-proto AddChartDialogReact

* disable user_add HighlightedFeatureChart

* more mui, tweak theme to be denser

* histogram taller, fix off-by-one error in width, debounce slider value

* fix empty param array in useParamColumnsExperimental

* type spec ("_multi_column:number") etc understood by ColumnSelectionComponent

* some visual layout, placeholder extra_controls, state needs fixing

* add zod

* start trying to use zod/mobx combo for config state

* easier to use generic for DataColumn

* somewhat functional AddChart as long as no extra_props etc

* Revert "easier to use generic for DataColumn"

This reverts commit 26a5046.

* new AddChartDialog seems to work, may be edge-cases with extra_controls

* only use new AddChart, displayed non-modal

* infer default values and preserve column selection where compatible

* dropdown apply default number limit on filterOptions

* export DropdownAutocompleteComponent (always as observer)

* experimental LinksComponent and useRowsAsColumnsLink etc

* add 'column' as a type of settings gui, use for colorBy

* some type changes - trying to define DataStore better, changing to use Chart interface in more places...

* fix useRangeFilter to use column.field rather than name

* increase width of dialog to fit content

* add "multicolumn" gui type, WIP

* some changes to documentation, return added column objects

* add hooks for reacting to rows_as_columns_link, show in selection dialog

* memoize histogram points

* add filter to ForeignRows component

* fix bug with destructuring null link

* fix bug in previous commit that lead to excess re-running of column loading hook

* fix (yet again) desctructuring possibily null

* loadColumnSet with empty array is no-op

* some experimental WIP and comments not visible to end-user

* fix loadColumnSet should still call callback for empty columns

* somewhat more presentable ForeignRows, add control for max

* don't show AddChart in modal

* Jh dev exp 1 -version 0 (#106)

* exp 27 [deploy]

* Increment version to 1.37 [skip ci]

* debugging statements (add_datasource)  [deploy]

* exp 28 [deploy]

* Increment version to 1.38 [skip ci]

* skip_column_clean - set to true, and replace =False , for add_datasource function

* fileuplaod.tsx from main

* verified mdvproject, server.py files

* modulare code changes- add_update_image_datasource

* incremental chanhge for default ds - upload  feature

* exp 28 [deploy]

* Increment version to 1.39 [skip ci]

* unitesting modifications- add_or update datasource

* server unittestchange

* catalog-dbservice functions

* db changes, accessed and uopdated tiestamp changes

* bia catalog [deploy]

* bia catalog1 [deploy]

* Increment version to 1.40 [skip ci]

* show datatype along with column name

* rename pjt-dev -> dev

* revert accidental commit of local port change

* SVGChart axis settings in folder. Improve error display in SettingsDialogComponent

* add (hard-coded) fraction tooltip to DotPlot

* slightly fancier tooltip

* remove unused local variables

* tooltip moves with pointer

* StackedRowChart tooltip

* enhanced frotend with themes [deploy]

* Increment version to 1.41 [skip ci]

* fix import only used as type

* fix dim.destroy() on unmount rather than removeFilter

* remove duplicate "Selection Dialog" in chart types dict.

* fix workaround deck bug with changing size of colorRange array

* minor refactor and change name of "Density Visualisation" settings in gui

* fix behaviour of resetButton in viv chart

* Enhance accessibility, readability, and color palette consistency in CSS

* Adjusted dark mode styling for better user experience

* Implemented ThemeProvider component to handle theme configuration across the app

* Updated catalog_index to wrap Dashboard in CustomThemeProvider

* Updated Dashboard to use ThemeProvider

* style tweaks

* (feat) add columns to SelectionDialog at runtime. ColumnSelectionComponent should be reusable elsewhere.

* (feat) delete filters at runtime

* (feat) add histogram to range filters

* fix ugly vectorEffect on histogram when wide

* smaller icons in TextField

* (fix) make sure scale bar is on top of other layers

* remove some dead code & move resizable icon somewhat out of way

* fix Histogram rendering - no useMemo for now???

* fix (partial) spatial selections work on images with different scale.

* fix bug when deleting text filters

* fix histogram generation, add some comments on Dimension classes

* show datatype along with column name

* rename pjt-dev -> dev

* revert accidental commit of local port change

* SVGChart axis settings in folder. Improve error display in SettingsDialogComponent

* add (hard-coded) fraction tooltip to DotPlot

* slightly fancier tooltip

* remove unused local variables

* tooltip moves with pointer

* StackedRowChart tooltip

* add blockquote stying for use in TextBoxChart

* change fraction to percentage in tooltip

* change vite config, custom onLog handling seems less relevant now

* fix issue with some environments not serving project json files

* bug fix- tiff image upload (view nort generated for a new imgae in a new project)

* Revert "rename pjt-dev -> dev"

This reverts commit bef69a2.

* hotfix for issue with js build

* fix image_view_prototype and some logic for upload

* add dist_hotfix js build

* remove old build

* new build from latest frontend

* protypre, example usage removed

* bia [deploy]

* Increment version to 1.42 [skip ci]

* bia2 [deploy]

* bia2 [deploy]

* Increment version to 1.43 [skip ci]

* show datatype along with column name

* rename pjt-dev -> dev

* revert accidental commit of local port change

* SVGChart axis settings in folder. Improve error display in SettingsDialogComponent

* add (hard-coded) fraction tooltip to DotPlot

* slightly fancier tooltip

* remove unused local variables

* tooltip moves with pointer

* StackedRowChart tooltip

* add blockquote stying for use in TextBoxChart

* change fraction to percentage in tooltip

* change vite config, custom onLog handling seems less relevant now

* fix issue with some environments not serving project json files

* Revert "rename pjt-dev -> dev"

This reverts commit bef69a2.

* fix home button route and don't save

* /app or update image datasource, upload to filepath modified

* temp-fileuploaddialog

* upload backed feature- added db operations

* temp-fileuploaddialog

* upload backed feature- added db operations

* modified backend add datasource validate file

* incremental changes -/add_or_update_image_datasource

* incremental changes Feature uplaod [deploy]

* exp 27 [deploy]

* debugging statements (add_datasource)  [deploy]

* Increment version to 1.37 [skip ci]

* exp 28 [deploy]

* skip_column_clean - set to true, and replace =False , for add_datasource function

* Increment version to 1.38 [skip ci]

* rebase operation - hopefully ok

* verified mdvproject, server.py files

* modulare code changes- add_update_image_datasource

* incremental chanhge for default ds - upload  feature

* exp 28 [deploy]

* unitesting modifications- add_or update datasource

* Increment version to 1.39 [skip ci]

* server unittestchange

* catalog-dbservice functions

* db changes, accessed and uopdated tiestamp changes

* bia catalog [deploy]

* bia catalog1 [deploy]

* Increment version to 1.40 [skip ci]

* rename pjt-dev -> dev

* revert accidental commit of local port change

* enhanced frotend with themes [deploy]

* Increment version to 1.41 [skip ci]

* bug fix- tiff image upload (view nort generated for a new imgae in a new project)

* hotfix for issue with js build

* fix image_view_prototype and some logic for upload

* add dist_hotfix js build

* rename pjt-dev -> dev

* revert accidental commit of local port change

* Revert "rename pjt-dev -> dev"

This reverts commit bef69a2.

* remove old build

* new build from latest frontend

* protypre, example usage removed

* bia [deploy]

* bia2 [deploy]

* Increment version to 1.42 [skip ci]

* bia2 [deploy]

* Increment version to 1.43 [skip ci]

* local developement (using hardcoded credentials , updated compose)

* docker secrets updated

* use mui Dialog

* dev server runs in docker with "npm run docker_dev"

* navigate to new image view on upload

* Improve error handling for useProjects hook operations

* Created ProjectErrorModal component

* Update dashboard error handling with ProjectErrorModal integration

* Update FileUploadDialog upload management to handle 403 errors

* secrets (flexibility added- env or run/secrets).
default view name removed-keeping region name as view name

* Created ProjectListView Component

* Incorporated Project List View to Dashboard

* Added projectUtils for sorting utility functions

* Updated Dashboard improve project sorting with asc and desc order support

* show datatype along with column name

* rename pjt-dev -> dev

* revert accidental commit of local port change

* SVGChart axis settings in folder. Improve error display in SettingsDialogComponent

* add (hard-coded) fraction tooltip to DotPlot

* slightly fancier tooltip

* remove unused local variables

* tooltip moves with pointer

* StackedRowChart tooltip

* add blockquote stying for use in TextBoxChart

* change fraction to percentage in tooltip

* change vite config, custom onLog handling seems less relevant now

* fix issue with some environments not serving project json files

* Revert "rename pjt-dev -> dev"

This reverts commit bef69a2.

* fix home button route and don't save

* adjust position of delete icon

* minor tweaks to types, show chart json options in non-dev

* fix bug with ColumnSelection and columns added via link, add filter support

* disable allow_user_add for ReactWordCloudChart

* lint add missing dependency

* fix ChartType.required signature & add `allow_user_add`

* first proto-proto AddChartDialogReact

* disable user_add HighlightedFeatureChart

* more mui, tweak theme to be denser

* histogram taller, fix off-by-one error in width, debounce slider value

* fix empty param array in useParamColumnsExperimental

* type spec ("_multi_column:number") etc understood by ColumnSelectionComponent

* some visual layout, placeholder extra_controls, state needs fixing

* add zod

* start trying to use zod/mobx combo for config state

* easier to use generic for DataColumn

* somewhat functional AddChart as long as no extra_props etc

* Revert "easier to use generic for DataColumn"

This reverts commit 26a5046.

* new AddChartDialog seems to work, may be edge-cases with extra_controls

* only use new AddChart, displayed non-modal

* infer default values and preserve column selection where compatible

* dropdown apply default number limit on filterOptions

* export DropdownAutocompleteComponent (always as observer)

* experimental LinksComponent and useRowsAsColumnsLink etc

* add 'column' as a type of settings gui, use for colorBy

* some type changes - trying to define DataStore better, changing to use Chart interface in more places...

* fix useRangeFilter to use column.field rather than name

* increase width of dialog to fit content

* add "multicolumn" gui type, WIP

* some changes to documentation, return added column objects

* add hooks for reacting to rows_as_columns_link, show in selection dialog

* memoize histogram points

* add filter to ForeignRows component

* fix bug with destructuring null link

* fix bug in previous commit that lead to excess re-running of column loading hook

* fix (yet again) desctructuring possibily null

* loadColumnSet with empty array is no-op

* some experimental WIP and comments not visible to end-user

* fix loadColumnSet should still call callback for empty columns

* somewhat more presentable ForeignRows, add control for max

* don't show AddChart in modal

* add_ds modified

* PR 103 [deploy]

* pR 103 [deploy]

* Increment version to 1.44 [skip ci]

* Update expected response status for CreateProject to align with backend changes

* Apply Biome formatting to useProjects hook for code consistency

* Applied Biome formatting to catalog files to ensure code consistency

* new pr 103 [deploy]

* Increment version to 1.45 [skip ci]

* latest build pf npm run build-flask-docker-hotfix

* pr 103 new build [deploy]

* Increment version to 1.46 [skip ci]

* cleanup db section [deploy]

* cleanup and blueprint backend added [deploy]

* Increment version to 1.47 [skip ci]

* for single project , new  project_bp introduced with access_level popped from route options

* fix docker config to build js at runtime. remove some print statements in mdvproject

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Peter Todd <[email protected]>
Co-authored-by: Alejandro Quijada Leyton <[email protected]>
Co-authored-by: Peter Todd <[email protected]>

* fix (hopefully) vite-build as used by netlify to use more memory

* Added enhanced file upload support to FileUploadDialogComponent

* flag backedn_db in mdvproject, added chart for csv upload

* cicd file removed [deploy]

* docler deploy version [deploy]

* Increment version to 1.48 [skip ci]

* add missing dtype entry for "text16"

* Interactive histogram in Selection Dialog (#109)

* histogram brushX like behaviour

* encapsulate histogram mouse behaviour in hook

* feature: histogram hide sliders & text-inputs, improve behavior

* add simple label for current values

* fix clamp normalisedX in histogram getX

* Updated chart.css for consistency between ciview-chart-panel definitions (#108)

* pr dev merged [deploy]

* Increment version to 1.49 [skip ci]

* Feature/upload 154  (#110)

* flag backedn_db in mdvproject, added chart for csv upload

* cicd file removed [deploy]

* docler deploy version [deploy]

* Increment version to 1.48 [skip ci]

* pr dev merged [deploy]

* Increment version to 1.49 [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* modified cicd

* Strict typescript config (#112)

* tsconfig strictNullChecks - not tested running, some errors remain to be addressed

* cancel getSettings in unused ReactWordCloudChart

* add LoadedDataColumn type and some other type tweaks/documentation

* convert Utilities.js to ts, add type predicate logic

* minor type refactor, don't redeclare Project, satisfy ts strict mode

* tsconfig "strict", still some relaxed rules. remaining errors are things I want to fix

* add @types/papaparse

* update react-xml-viewer

* update uuid

* fix runtime error when tiff preview is added

* useCallback for listeners, not sure how much it matters here

* type-only changes to annotations/comments

* add some type helpers, minor refactor

* tsconfig remove no implicit any

* refactor helper hook for dropdown, some other type changes

* more consistent use of FieldName, improve GuiSpec type inference particularly for ranges

* add some types, handle nulls and 'ts-expect-error' for things that need attention in future

* fix workaround issue with density layers not working when extensions are used (different shapes)

* some use of `g` type helper, comments on non-reactive contour parameter

* comments, clean up imports

* disable broken multicolumn setting in selection dialog

* convert BaseChart to TS, use that as defacto Chart type. try to improve `GuiSpecs` definition

* fix bug with missing values for contour parameter

* some more type testing

* fix build - remove '.js' extension from BaseChart imports. shouldn't be necessary.

* npm audit fix because of issue with cross-spawn

* feature: useBrushX uses d3.brushX

* fix implicit any

* ruff corrections

* cicd name changed

* cd pipleine updated- to have trigger condition as on PR closed

* test commit (#113)

* Increment version to 1.50 [skip ci]

* fix static page generation

* File upload UI TSV support (#114)

* Refactored csvWorker to DatasourceWorker to allow for the processing of tsv and csv files

* Added support for tsv files in FileUploadDialog

* fix use absolute import of View to help sphinx parser

* fix nested `"""` broke parser

* fix typo / add comment

* Added first version of login component

* Added MDV logo to login assets

* Add HTML file for login entry point

* Updated vite config to define login entry point

* Updated login redirect route to /auth/login

* auth bug fixing in login

* Updated redirection url in login component

* Added login html document to backend templates

* Add login route to serve login page at /login_dev

* session clear in login

* redirect url change

* routes protected in auth

* Added SSO login button

* inivtation script

* Updated SSO text

* sso login added

* deploy cicd updated

* cicd bia deploy [deploy]

* exp [deploy]

* cicd

* exp 512 [deploy]

* cicid deploy

* deploy

* exp 12 [deploy]

* basechart issue modification [deploy]

* Increment version to 1.58 [skip ci]

* lock file

* docker file updated

* clutter

* clutter

* auth latest [deploy]

* Increment version to 1.59 [skip ci]

* url_for issue fixed (deploy)

* exp 2 [deploy]

* Increment version to 1.60 [skip ci]

* session management

* enable_auth flag flexibility added

* logout frontend added

* config related modifications

* auth_flag [deploy]

* Increment version to 1.61 [skip ci]

* logout, is_authenticated added

* auth0 modifications

* bia auth [deploy]

* Increment version to 1.62 [skip ci]

* /logout - / removed  [deploy]

* auth - /logout [deploy]

* Increment version to 1.63 [skip ci]

* logour turl added

* exp as [deploy]

* Increment version to 1.64 [skip ci]

* get user added

* bis user display [deploy]

* Increment version to 1.65 [skip ci]

* app secret key readinfg from env variable

* deploy cicid modified

* yml file modified

* ununsed file deleted

* 5170 removed from yml file

* volumes modified as per dev

* docker secrets pgadmin added

* ident

---------

Co-authored-by: jayeshhireox <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Alejandro Quijada Leyton <[email protected]>
Co-authored-by: jayeshhireox <[email protected]>
Co-authored-by: nawabfurquan <[email protected]>
Co-authored-by: Furquan <[email protected]>
  • Loading branch information
7 people authored Feb 19, 2025
1 parent 44e9ee8 commit 6e85634
Show file tree
Hide file tree
Showing 24 changed files with 1,131 additions and 101 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ data/
xvenv/
secrets/
app_logs/
docker-secrets-local.yml


# as seen inside devcontainer
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ RUN poetry config virtualenvs.create false
#16 20.81 In a future version of Poetry this warning will become an error!
# seems to be ok to first install with --no-root for dependencies, then install the root package later
# we don't want to set package-mode = false in pyproject.toml
RUN poetry install --with dev,backend --no-root
RUN poetry install --with dev,backend,auth --no-root

WORKDIR /app
# copy the package.json and package-lock.json as a separate step so npm install can be cached
Expand All @@ -56,7 +56,7 @@ WORKDIR /app/python
# installing again so we have mdvtools as a module, on top of the previous install layer with dependencies
# this step should be very fast
# if we don't have this, the server itself runs, but anything that doesn't run from this workdir will fail to import mdvtools
RUN poetry install --with dev,backend
RUN poetry install --with dev,backend,auth

# Expose the port that Flask will run on
EXPOSE 5055
Expand Down
34 changes: 30 additions & 4 deletions docker-secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,35 @@ services:
- ./app_logs:/app/logs
# - ~/data:/app/data # thinking about allowing symlinked data folder...
environment:
# Flask configuration
- FLASK_ENV=production # Set environment to production
- PYTHONUNBUFFERED=1
# Hardcoded database connection details

# Database connection details
- DB_USER=my_db_user
- DB_PASSWORD=my_db_password
- DB_NAME=my_db_name
- DB_HOST=mdv_db # Assuming the db service name is mdv_db, change if necessary
- DB_HOST=mdv_db # Assuming the db service name is mdv_db

- FLASK_SECRET_KEY=abc
# Auth0 configuration
#LOGIN_REDIRECT_URL="https://bia.cmd.ox.ac.uk/carroll/login_dev"
- LOGIN_REDIRECT_URL=https://localhost:5055/login_dev
- ENABLE_AUTH=0 # Set to 0 to disable authentication
- AUTH0_DOMAIN=dev-gvt7choa0j53kaa1.us.auth0.com
- AUTH0_CLIENT_ID=Y3qwB7AhV7ZzJjlOb7AkYfbbme7jfNer
- AUTH0_CLIENT_SECRET=None
- AUTH0_CALLBACK_URL=http://localhost:5055/callback
#AUTH0_CALLBACK_URL=http://bia.cmd.ox.ac.uk/carroll/callback
- AUTH0_AUDIENCE=https://mdv.dev/api

# JWT configuration for Auth0 (for token validation)
- AUTH0_PUBLIC_KEY_URI=https://dev-gvt7choa0j53kaa1.us.auth0.com/.well-known/jwks.json # JWKS URI to get Auth0 public keys for JWT validation

# Shibboleth configuration
- SHIBBOLETH_LOGIN_URL=https://bia.cmd.ox.ac.uk/Shibboleth.sso/Login
- SHIBBOLETH_LOGOUT_URL=https://bia.cmd.ox.ac.uk/Shibboleth.sso/Logout

depends_on:
- mdv_db

Expand All @@ -46,7 +68,7 @@ services:
- pgadmin-data:/var/lib/pgadmin
depends_on:
- mdv_db

mdv_db:
image: postgres:16
volumes:
Expand All @@ -59,4 +81,8 @@ services:
volumes:
postgres-data:
mdv-data:
pgadmin-data:
pgadmin-data:

#secrets:
# auth0_client_secret:
# file: ./secrets/auth0_client_secret.txt
11 changes: 11 additions & 0 deletions login_dev.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login</title>
</head>
<body>
<script type="module" src="/src/login/login_index.tsx"></script>
</body>
</html>
285 changes: 285 additions & 0 deletions python/mdvtools/auth/auth0_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import time
import requests
from authlib.integrations.flask_client import OAuth
from flask import session, redirect, url_for
from typing import Optional
from mdvtools.auth.auth_provider import AuthProvider
import logging
from jose import jwt
from jose.exceptions import ExpiredSignatureError, JWTError, JWTClaimsError



class Auth0Provider(AuthProvider):
def __init__(self, app, oauth: OAuth, client_id: str, client_secret: str, domain: str):
"""
Initializes the Auth0Provider class with application details.
:param app: Flask app instance
:param oauth: Authlib OAuth instance
:param client_id: Auth0 Client ID
:param client_secret: Auth0 Client Secret
:param domain: Auth0 Domain
"""
try:
if not all([client_id, client_secret, domain]):
raise ValueError("Missing required Auth0 configuration parameters.")

self.app = app
self.oauth = oauth
self.client_id = client_id
self.client_secret = client_secret
self.domain = domain

self._initialize_oauth()
logging.info("Auth0Provider initialized successfully.")
except Exception as e:
logging.critical(f"Failed to initialize Auth0Provider: {e}")
raise

def _initialize_oauth(self):
"""
Registers the Auth0 OAuth provider and validates OpenID Connect metadata.
"""
try:
# Construct the server metadata URL for OpenID Connect discovery
server_metadata_url = f'https://{self.domain}/.well-known/openid-configuration'

# Attempt to fetch metadata to ensure it's accessible
response = requests.get(server_metadata_url)
if response.status_code != 200:
logging.error(f"Failed to fetch OpenID configuration from {server_metadata_url}: {response.text}")
raise RuntimeError(f"Unable to fetch OpenID Connect metadata from {server_metadata_url}")

# Parse and check the existence of jwks_uri in the metadata
metadata = response.json()
print("@@@@@@")
#print(metadata)
jwks_uri = metadata.get('jwks_uri')
if not jwks_uri:
logging.error(f"The OpenID configuration is missing 'jwks_uri': {metadata}")
raise RuntimeError("'jwks_uri' is missing in OpenID Connect metadata.")

# Register the OAuth provider with server_metadata_url for dynamic metadata fetching
self.oauth.register(
'auth0',
client_id=self.client_id,
client_secret=self.client_secret,
server_metadata_url=server_metadata_url,
client_kwargs={'scope': 'openid profile email'},
)
logging.info("Auth0 OAuth provider registered successfully with OpenID Connect metadata.")
except Exception as e:
logging.error(f"Error while registering OAuth provider: {e}")
raise RuntimeError("Failed to initialize OAuth.") from e

def login(self) -> str:
"""
Initiates the login process by redirecting to Auth0's authorization page.
"""
try:
print("$$$$$$$$$$$$$$$ -login-1")
logging.info("Initiating login process.")
#redirect_uri = url_for('callback', _external=True)
redirect_uri = self.app.config["AUTH0_CALLBACK_URL"]
print(redirect_uri)
print(self.oauth.auth0.authorize_redirect(redirect_uri))
return self.oauth.auth0.authorize_redirect(redirect_uri=redirect_uri)

except Exception as e:
logging.error(f"Error during login process: {e}")
raise RuntimeError("Login failed.") from e

def logout(self) -> None:
"""
Logs the user out by clearing the session and redirecting to Auth0's logout endpoint.
"""
try:
logging.info("Logging out user from Auth0.")

# Clear the server-side session to remove any stored tokens and user data
session.clear()

# Prepare the redirect URL after logout (i.e., where the user is sent after logging out of Auth0)
redirect_url = self.app.config["LOGIN_REDIRECT_URL"] # The URL to redirect after logout

# Redirect the user to Auth0's logout URL, which will handle the Auth0-side logout
# This will log the user out of Auth0 and redirect them to the provided URL
logout_url = f"https://{self.app.config['AUTH0_DOMAIN']}/v2/logout?returnTo={redirect_url}&client_id={self.app.config['AUTH0_CLIENT_ID']}"

logging.info(f"Redirecting to Auth0 logout URL: {logout_url}")
return redirect(logout_url)

except Exception as e:
logging.error(f"Error during logout process: {e}")
raise RuntimeError("Auth0 logout failed.") from e


def get_user(self, token: dict) -> Optional[dict]:
"""
Retrieves the user information using the provided token.
:param token: Dictionary containing access token and user details
:return: User information dictionary or None
"""
try:
logging.info("Fetching user information.")

# Extract access token
access_token = token.get("access_token")
if not access_token:
logging.error("Access token is missing.")
return None

# Correct Authorization Header
headers = {"Authorization": f"Bearer {access_token}"}

user_info_url = f"https://{self.domain}/userinfo"
response = requests.get(user_info_url, headers=headers)

if response.status_code == 200:
logging.debug("User information retrieved successfully.")
raw_data = response.json()

# Transform response to match frontend expectations
user_data = {
"name": raw_data.get("name", "Unknown User"),
"email": raw_data.get("email", ""),
"association": "Example Corp", # Static or extract from another source
"avatarUrl": raw_data.get("picture", ""),
}
print("!!!!!!")
print(user_data)
return user_data
else:
logging.warning(f"Failed to fetch user information: {response.status_code} {response.text}")
return None

except requests.RequestException as e:
logging.error(f"Error while fetching user information: {e}")
return None

def get_token(self) -> Optional[str]:
"""
Retrieves the token from the session.
:return: Token string or None
"""
try:
logging.info("Retrieving token from session.")
return session.get('token', {}).get('access_token')
except Exception as e:
logging.error(f"Error while retrieving token: {e}")
return None

def handle_callback(self) -> Optional[str]:
"""
Handles the Auth0 callback and retrieves the access token.
:return: Access token string
"""
try:
logging.info("Handling callback from Auth0.")
token = self.oauth.auth0.authorize_access_token()
if 'access_token' not in token:
raise ValueError("Access token not found in the response.")
session['token'] = token
session["auth_method"] = "auth0"
logging.info("Access token retrieved and stored in session.")
return token['access_token']
except Exception as e:
logging.error(f"Error during callback handling: {e}")
session.clear() # Clear session in case of failure
raise RuntimeError("Callback handling failed.") from e

def is_authenticated(self, token: str) -> bool:
"""
Checks if the user is authenticated by verifying the token.
:param token: Access token
:return: True if authenticated, False otherwise
"""
try:
logging.info("Checking authentication status.")
user_info = self.get_user(token)
return user_info is not None
except Exception as e:
logging.error(f"Error while checking authentication: {e}")
return False

def is_token_valid(self, token):
"""
Validates the provided token by verifying its signature using Auth0's public keys
and ensuring it's not expired.
"""
try:
# Step 1: Decode the token header without verification to extract the 'kid'
unverified_header = jwt.get_unverified_header(token)

if unverified_header is None:
print("++++++++1")
logging.error("Invalid token header.")
return False

# Step 2: Get the public key from Auth0's JWKS (JSON Web Key Set) endpoint
rsa_key = {}
if 'kid' in unverified_header:
try:
# Fetch Auth0 public keys from jwks_uri
response = requests.get(self.app.config['AUTH0_PUBLIC_KEY_URI'])
if response.status_code != 200:
logging.error(f"Failed to fetch JWKS: {response.status_code}")
print("++++++++2")
return False
jwks = response.json()

# Find the key in the JWKS that matches the 'kid' in the token header
for key in jwks['keys']:
if key['kid'] == unverified_header['kid']:
rsa_key = {
'kty': key['kty'],
'kid': key['kid'],
'use': key['use'],
'n': key['n'],
'e': key['e']
}
break
except Exception as e:
logging.error(f"Error getting public keys from Auth0: {e}")
return False

if not rsa_key:
logging.error("No valid key found in JWKS for token verification.")
print("++++++++3")
return False

# Step 3: Verify the JWT token using the public key
payload = jwt.decode(
token,
rsa_key,
algorithms=["RS256"],
audience=self.app.config["AUTH0_AUDIENCE"], # Your API audience
issuer=f"https://{self.app.config['AUTH0_DOMAIN']}/"
)
print("++++++++4")
# Step 4: Check the expiration of the token
if payload['exp'] > time.time():
print("++++++++5")
return True
else:
logging.error("Token is expired.")
print("++++++++6")
return False

except ExpiredSignatureError:
logging.error("Token is expired.")
return False
except JWTClaimsError:
logging.error("Invalid claims in token.")
return False
except JWTError as e:
logging.error(f"Error decoding token: {e}")
return False
except Exception as e:
logging.error(f"Error during token validation: {e}")
return False
Loading

0 comments on commit 6e85634

Please sign in to comment.