feat: add idpyoidc server initialization
This commit is contained in:
parent
02b75a3eca
commit
2426e0675c
2 changed files with 192 additions and 0 deletions
136
src/fastapi_oidc_op/oidc/provider.py
Normal file
136
src/fastapi_oidc_op/oidc/provider.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
"""idpyoidc Server initialization."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from idpyoidc.server import Server
|
||||
|
||||
from fastapi_oidc_op.config import Settings
|
||||
from fastapi_oidc_op.oidc.claims import PorchlightUserInfo
|
||||
|
||||
|
||||
def _build_server_config(settings: Settings) -> dict:
|
||||
"""Build the idpyoidc configuration dict from application settings."""
|
||||
key_path = Path(settings.signing_key_path)
|
||||
key_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return {
|
||||
"issuer": settings.issuer,
|
||||
"key_conf": {
|
||||
"key_defs": [
|
||||
{"type": "RSA", "use": ["sig"]},
|
||||
{"type": "EC", "crv": "P-256", "use": ["sig"]},
|
||||
],
|
||||
"private_path": str(key_path / "private_jwks.json"),
|
||||
"public_path": str(key_path / "public_jwks.json"),
|
||||
"uri_path": "jwks",
|
||||
"read_only": False,
|
||||
},
|
||||
"endpoint": {
|
||||
"provider_config": {
|
||||
"path": ".well-known/openid-configuration",
|
||||
"class": "idpyoidc.server.oidc.provider_config.ProviderConfiguration",
|
||||
"kwargs": {},
|
||||
},
|
||||
"authorization": {
|
||||
"path": "authorization",
|
||||
"class": "idpyoidc.server.oidc.authorization.Authorization",
|
||||
"kwargs": {},
|
||||
},
|
||||
"token": {
|
||||
"path": "token",
|
||||
"class": "idpyoidc.server.oidc.token.Token",
|
||||
"kwargs": {},
|
||||
},
|
||||
"userinfo": {
|
||||
"path": "userinfo",
|
||||
"class": "idpyoidc.server.oidc.userinfo.UserInfo",
|
||||
"kwargs": {},
|
||||
},
|
||||
},
|
||||
"userinfo": {
|
||||
"class": PorchlightUserInfo,
|
||||
"kwargs": {},
|
||||
},
|
||||
"authz": {
|
||||
"class": "idpyoidc.server.authz.AuthzHandling",
|
||||
"kwargs": {
|
||||
"grant_config": {
|
||||
"usage_rules": {
|
||||
"authorization_code": {
|
||||
"supports_minting": ["access_token", "refresh_token", "id_token"],
|
||||
"max_usage": 1,
|
||||
"expires_in": 120,
|
||||
},
|
||||
"access_token": {
|
||||
"expires_in": 3600,
|
||||
},
|
||||
"refresh_token": {
|
||||
"supports_minting": ["access_token", "refresh_token", "id_token"],
|
||||
"expires_in": 86400,
|
||||
},
|
||||
},
|
||||
"expires_in": 2592000,
|
||||
},
|
||||
},
|
||||
},
|
||||
"token_handler_args": {
|
||||
"jwks_def": {
|
||||
"private_path": str(key_path / "token_jwks.json"),
|
||||
"read_only": False,
|
||||
"key_defs": [{"type": "oct", "bytes": "24", "use": ["enc"], "kid": "code"}],
|
||||
},
|
||||
"code": {
|
||||
"kwargs": {"lifetime": 600},
|
||||
},
|
||||
"token": {
|
||||
"class": "idpyoidc.server.token.jwt_token.JWTToken",
|
||||
"kwargs": {
|
||||
"lifetime": 3600,
|
||||
"add_claims_by_scope": True,
|
||||
"aud": [settings.issuer],
|
||||
},
|
||||
},
|
||||
"refresh": {
|
||||
"class": "idpyoidc.server.token.jwt_token.JWTToken",
|
||||
"kwargs": {
|
||||
"lifetime": 86400,
|
||||
"aud": [settings.issuer],
|
||||
},
|
||||
},
|
||||
"id_token": {
|
||||
"class": "idpyoidc.server.token.id_token.IDToken",
|
||||
"kwargs": {
|
||||
"lifetime": 3600,
|
||||
},
|
||||
},
|
||||
},
|
||||
"scopes_to_claims": {
|
||||
"openid": ["sub"],
|
||||
"profile": [
|
||||
"name",
|
||||
"given_name",
|
||||
"family_name",
|
||||
"middle_name",
|
||||
"nickname",
|
||||
"profile",
|
||||
"picture",
|
||||
"website",
|
||||
"gender",
|
||||
"birthdate",
|
||||
"zoneinfo",
|
||||
"locale",
|
||||
"updated_at",
|
||||
"preferred_username",
|
||||
],
|
||||
"email": ["email", "email_verified"],
|
||||
"phone": ["phone_number", "phone_number_verified"],
|
||||
},
|
||||
"authentication": {},
|
||||
}
|
||||
|
||||
|
||||
def create_oidc_server(settings: Settings) -> Server:
|
||||
"""Create and configure an idpyoidc Server instance."""
|
||||
config = _build_server_config(settings)
|
||||
server = Server(conf=config)
|
||||
return server
|
||||
56
tests/test_oidc/test_provider.py
Normal file
56
tests/test_oidc/test_provider.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi_oidc_op.config import Settings
|
||||
from fastapi_oidc_op.oidc.provider import create_oidc_server
|
||||
|
||||
|
||||
def test_create_server_has_endpoints() -> None:
|
||||
key_path = Path("test_keys_provider")
|
||||
key_path.mkdir(exist_ok=True)
|
||||
try:
|
||||
settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path))
|
||||
server = create_oidc_server(settings)
|
||||
assert "authorization" in server.endpoint
|
||||
assert "token" in server.endpoint
|
||||
assert "userinfo" in server.endpoint
|
||||
assert "provider_config" in server.endpoint
|
||||
finally:
|
||||
shutil.rmtree(key_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_create_server_has_issuer() -> None:
|
||||
key_path = Path("test_keys_issuer")
|
||||
key_path.mkdir(exist_ok=True)
|
||||
try:
|
||||
settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path))
|
||||
server = create_oidc_server(settings)
|
||||
assert server.context.issuer == "http://localhost:8000"
|
||||
finally:
|
||||
shutil.rmtree(key_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_create_server_jwks_available() -> None:
|
||||
key_path = Path("test_keys_jwks")
|
||||
key_path.mkdir(exist_ok=True)
|
||||
try:
|
||||
settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path))
|
||||
server = create_oidc_server(settings)
|
||||
keys = server.keyjar.export_jwks()
|
||||
assert "keys" in keys
|
||||
assert len(keys["keys"]) > 0
|
||||
finally:
|
||||
shutil.rmtree(key_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_create_server_userinfo_is_porchlight() -> None:
|
||||
key_path = Path("test_keys_userinfo")
|
||||
key_path.mkdir(exist_ok=True)
|
||||
try:
|
||||
settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path))
|
||||
server = create_oidc_server(settings)
|
||||
from fastapi_oidc_op.oidc.claims import PorchlightUserInfo
|
||||
|
||||
assert isinstance(server.context.userinfo, PorchlightUserInfo)
|
||||
finally:
|
||||
shutil.rmtree(key_path, ignore_errors=True)
|
||||
Loading…
Add table
Add a link
Reference in a new issue