feat: Login with OAuth via OpenID Connect (OIDC) (#3280)

* initial oidc implementation

* add dynamic scheme

* e2e test setup

* add caching

* fix

* try this

* add libldap-2.5 to runtime dependencies (#2849)

* New translations en-us.json (Norwegian) (#2851)

* New Crowdin updates (#2855)

* New translations en-us.json (Italian)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Portuguese)

* fix

* remove cache

* cache yarn deps

* cache docker image

* cleanup action

* lint

* fix tests

* remove not needed variables

* run code gen

* fix tests

* add docs

* move code into custom scheme

* remove unneeded type

* fix oidc admin

* add more tests

* add better spacing on login page

* create auth providers

* clean up testing stuff

* type fixes

* add OIDC auth method to postgres enum

* add option to bypass login screen and go directly to iDP

* remove check so we can fallback to another auth method oauth fails

* Add provider name to be shown at the login screen

* add new properties to admin about api

* fix spec

* add a prompt to change auth method when changing password

* Create new auth section. Add more info on auth methods

* update docs

* run ruff

* update docs

* format

* docs gen

* formatting

* initialize logger in class

* mypy type fixes

* docs gen

* add models to get proper fields in docs and fix serialization

* validate id token before using it

* only request a mealie token on initial callback

* remove unused method

* fix unit tests

* docs gen

* check for valid idToken before getting token

* add iss to mealie token

* check to see if we already have a mealie token before getting one

* fix lock file

* update authlib

* update lock file

* add remember me environment variable

* add user group setting to allow only certain groups to log in

---------

Co-authored-by: Carter Mintey <cmintey8@gmail.com>
Co-authored-by: Carter <35710697+cmintey@users.noreply.github.com>
This commit is contained in:
Hayden
2024-03-10 13:51:36 -05:00
committed by GitHub
parent bea1a592d7
commit 5f6844eceb
53 changed files with 1533 additions and 400 deletions

View File

@@ -1,5 +1,5 @@
# This file is auto-generated by gen_schema_exports.py
from .about import AdminAboutInfo, AppInfo, AppStartupInfo, AppStatistics, AppTheme, CheckAppConfig
from .about import AdminAboutInfo, AppInfo, AppStartupInfo, AppStatistics, AppTheme, CheckAppConfig, OIDCInfo
from .backup import AllBackups, BackupFile, BackupOptions, CreateBackup, ImportJob
from .email import EmailReady, EmailSuccess, EmailTest
from .maintenance import MaintenanceLogs, MaintenanceStorageDetails, MaintenanceSummary
@@ -22,6 +22,11 @@ __all__ = [
"BackupOptions",
"CreateBackup",
"ImportJob",
"EmailReady",
"EmailSuccess",
"EmailTest",
"CustomPageBase",
"CustomPageOut",
"MaintenanceLogs",
"MaintenanceStorageDetails",
"MaintenanceSummary",
@@ -31,15 +36,7 @@ __all__ = [
"AppStatistics",
"AppTheme",
"CheckAppConfig",
"EmailReady",
"EmailSuccess",
"EmailTest",
"CustomPageBase",
"CustomPageOut",
"ChowdownURL",
"MigrationFile",
"MigrationImport",
"Migrations",
"OIDCInfo",
"CommentImport",
"CustomPageImport",
"GroupImport",
@@ -48,4 +45,8 @@ __all__ = [
"RecipeImport",
"SettingsImport",
"UserImport",
"ChowdownURL",
"MigrationFile",
"MigrationImport",
"Migrations",
]

View File

@@ -15,6 +15,9 @@ class AppInfo(MealieModel):
demo_status: bool
allow_signup: bool
default_group_slug: str | None = None
enable_oidc: bool
oidc_redirect: bool
oidc_provider_name: str
class AppTheme(MealieModel):
@@ -58,5 +61,11 @@ class AdminAboutInfo(AppInfo):
class CheckAppConfig(MealieModel):
email_ready: bool
ldap_ready: bool
oidc_ready: bool
base_url_set: bool
is_up_to_date: bool
class OIDCInfo(MealieModel):
configuration_url: str | None
client_id: str | None

View File

@@ -45,15 +45,12 @@ from .invite_token import CreateInviteToken, EmailInitationResponse, EmailInvita
from .webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination, WebhookType
__all__ = [
"CreateGroupPreferences",
"ReadGroupPreferences",
"UpdateGroupPreferences",
"GroupDataExport",
"CreateWebhook",
"ReadWebhook",
"SaveWebhook",
"WebhookPagination",
"WebhookType",
"GroupDataExport",
"GroupEventNotifierCreate",
"GroupEventNotifierOptions",
"GroupEventNotifierOptionsOut",
@@ -63,9 +60,21 @@ __all__ = [
"GroupEventNotifierSave",
"GroupEventNotifierUpdate",
"GroupEventPagination",
"CreateGroupPreferences",
"ReadGroupPreferences",
"UpdateGroupPreferences",
"GroupStatistics",
"GroupStorage",
"GroupAdminUpdate",
"DataMigrationCreate",
"SupportedMigrations",
"SeederConfig",
"SetPermissions",
"CreateInviteToken",
"EmailInitationResponse",
"EmailInvitation",
"ReadInviteToken",
"SaveInviteToken",
"ShoppingListAddRecipeParams",
"ShoppingListCreate",
"ShoppingListItemBase",
@@ -88,13 +97,4 @@ __all__ = [
"ShoppingListSave",
"ShoppingListSummary",
"ShoppingListUpdate",
"GroupAdminUpdate",
"SetPermissions",
"GroupStatistics",
"GroupStorage",
"CreateInviteToken",
"EmailInitationResponse",
"EmailInvitation",
"ReadInviteToken",
"SaveInviteToken",
]

View File

@@ -22,18 +22,6 @@ from .plan_rules import (
from .shopping_list import ListItem, ShoppingListIn, ShoppingListOut
__all__ = [
"CreatePlanEntry",
"CreateRandomEntry",
"PlanEntryPagination",
"PlanEntryType",
"ReadPlanEntry",
"SavePlanEntry",
"UpdatePlanEntry",
"MealDayIn",
"MealDayOut",
"MealIn",
"MealPlanIn",
"MealPlanOut",
"Category",
"PlanRulesCreate",
"PlanRulesDay",
@@ -42,7 +30,19 @@ __all__ = [
"PlanRulesSave",
"PlanRulesType",
"Tag",
"CreatePlanEntry",
"CreateRandomEntry",
"PlanEntryPagination",
"PlanEntryType",
"ReadPlanEntry",
"SavePlanEntry",
"UpdatePlanEntry",
"ListItem",
"ShoppingListIn",
"ShoppingListOut",
"MealDayIn",
"MealDayOut",
"MealIn",
"MealPlanIn",
"MealPlanOut",
]

View File

@@ -88,45 +88,8 @@ from .recipe_tool import RecipeToolCreate, RecipeToolOut, RecipeToolResponse, Re
from .request_helpers import RecipeDuplicate, RecipeSlug, RecipeZipTokenResponse, SlugResponse, UpdateImageResponse
__all__ = [
"RecipeToolCreate",
"RecipeToolOut",
"RecipeToolResponse",
"RecipeToolSave",
"RecipeTimelineEventCreate",
"RecipeTimelineEventIn",
"RecipeTimelineEventOut",
"RecipeTimelineEventPagination",
"RecipeTimelineEventUpdate",
"TimelineEventImage",
"TimelineEventType",
"RecipeAsset",
"Nutrition",
"RecipeSettings",
"RecipeShareToken",
"RecipeShareTokenCreate",
"RecipeShareTokenSave",
"RecipeShareTokenSummary",
"RecipeDuplicate",
"RecipeSlug",
"RecipeZipTokenResponse",
"SlugResponse",
"UpdateImageResponse",
"RecipeNote",
"CategoryBase",
"CategoryIn",
"CategoryOut",
"CategorySave",
"RecipeCategoryResponse",
"RecipeTagResponse",
"TagBase",
"TagIn",
"TagOut",
"TagSave",
"RecipeCommentCreate",
"RecipeCommentOut",
"RecipeCommentPagination",
"RecipeCommentSave",
"RecipeCommentUpdate",
"UserBase",
"AssignCategories",
"AssignSettings",
"AssignTags",
@@ -134,10 +97,12 @@ __all__ = [
"ExportBase",
"ExportRecipes",
"ExportTypes",
"IngredientReferences",
"RecipeStep",
"RecipeImageTypes",
"Nutrition",
"RecipeNote",
"RecipeDuplicate",
"RecipeSlug",
"RecipeZipTokenResponse",
"SlugResponse",
"UpdateImageResponse",
"CreateIngredientFood",
"CreateIngredientFoodAlias",
"CreateIngredientUnit",
@@ -160,6 +125,23 @@ __all__ = [
"SaveIngredientFood",
"SaveIngredientUnit",
"UnitFoodBase",
"ScrapeRecipe",
"ScrapeRecipeTest",
"RecipeImageTypes",
"IngredientReferences",
"RecipeStep",
"RecipeAsset",
"RecipeToolCreate",
"RecipeToolOut",
"RecipeToolResponse",
"RecipeToolSave",
"RecipeTimelineEventCreate",
"RecipeTimelineEventIn",
"RecipeTimelineEventOut",
"RecipeTimelineEventPagination",
"RecipeTimelineEventUpdate",
"TimelineEventImage",
"TimelineEventType",
"CreateRecipe",
"CreateRecipeBulk",
"CreateRecipeByUrlBulk",
@@ -173,6 +155,24 @@ __all__ = [
"RecipeTagPagination",
"RecipeTool",
"RecipeToolPagination",
"ScrapeRecipe",
"ScrapeRecipeTest",
"CategoryBase",
"CategoryIn",
"CategoryOut",
"CategorySave",
"RecipeCategoryResponse",
"RecipeTagResponse",
"TagBase",
"TagIn",
"TagOut",
"TagSave",
"RecipeCommentCreate",
"RecipeCommentOut",
"RecipeCommentPagination",
"RecipeCommentSave",
"RecipeCommentUpdate",
"UserBase",
"RecipeShareToken",
"RecipeShareTokenCreate",
"RecipeShareTokenSave",
"RecipeShareTokenSummary",
]

View File

@@ -6,10 +6,6 @@ from .responses import ErrorResponse, FileTokenResponse, SuccessResponse
from .validation import ValidationResponse
__all__ = [
"ErrorResponse",
"FileTokenResponse",
"SuccessResponse",
"SearchFilter",
"LogicalOperator",
"QueryFilter",
"QueryFilterComponent",
@@ -20,5 +16,9 @@ __all__ = [
"PaginationBase",
"PaginationQuery",
"RecipeSearchQuery",
"ErrorResponse",
"FileTokenResponse",
"SuccessResponse",
"ValidationResponse",
"SearchFilter",
]

View File

@@ -33,6 +33,12 @@ __all__ = [
"Token",
"TokenData",
"UnlockResults",
"ForgotPassword",
"PasswordResetToken",
"PrivatePasswordResetToken",
"ResetPassword",
"SavePasswordResetToken",
"ValidateResetToken",
"ChangePassword",
"CreateToken",
"DeleteTokenResponse",
@@ -49,10 +55,4 @@ __all__ = [
"UserIn",
"UserOut",
"UserPagination",
"ForgotPassword",
"PasswordResetToken",
"PrivatePasswordResetToken",
"ResetPassword",
"SavePasswordResetToken",
"ValidateResetToken",
]

View File

@@ -1,5 +1,6 @@
from typing import Annotated
from fastapi import Form
from pydantic import UUID4, BaseModel, StringConstraints
from mealie.schema._mealie.mealie_model import MealieModel
@@ -17,3 +18,22 @@ class TokenData(BaseModel):
class UnlockResults(MealieModel):
unlocked: int = 0
class CredentialsRequest(BaseModel):
username: str
password: str
remember_me: bool = False
class OIDCRequest(BaseModel):
id_token: str
class CredentialsRequestForm:
"""Class that represents a user's credentials from the login form"""
def __init__(self, username: str = Form(""), password: str = Form(""), remember_me: bool = Form(False)):
self.username = username
self.password = password
self.remember_me = remember_me