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