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
88 changes: 87 additions & 1 deletion src/authorizer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@
from .client import AuthorizerClient
from .exceptions import AuthorizerConnectionError, AuthorizerError
from .types import (
CLIENT_ASSERTION_TYPE_JWT_BEARER,
GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_REFRESH_TOKEN,
GRANT_TYPE_TOKEN_EXCHANGE,
TOKEN_TYPE_ACCESS_TOKEN,
TOKEN_TYPE_JWT,
AddEmailTemplateRequest,
AddOrgMemberRequest,
AddTrustedIssuerRequest,
AddWebhookRequest,
AdminLoginRequest,
AdminMeta,
Expand All @@ -18,6 +27,16 @@
AuthToken,
CheckPermissionsRequest,
CheckPermissionsResponse,
Client,
ClientRequest,
ClientsResponse,
CreateClientRequest,
CreateClientResponse,
CreateOrganizationRequest,
CreateOrgOIDCConnectionRequest,
CreateOrgSAMLConnectionRequest,
CreateScimEndpointRequest,
CreateScimEndpointResponse,
DeleteEmailTemplateRequest,
DeleteUserRequest,
EmailTemplate,
Expand All @@ -44,32 +63,56 @@
InviteMembersRequest,
InviteMembersResponse,
ListAuditLogRequest,
ListClientsRequest,
ListOrganizationsRequest,
ListOrgMembersRequest,
ListPermissionsRequest,
ListPermissionsResponse,
ListTrustedIssuersRequest,
ListWebhookLogRequest,
LoginRequest,
MagicLinkLoginRequest,
MetaData,
OAuthProviders,
Organization,
OrganizationRequest,
OrganizationsResponse,
OrgMember,
OrgMembersResponse,
OrgOIDCConnection,
OrgOIDCConnectionRequest,
OrgSAMLConnection,
OrgSAMLConnectionRequest,
PaginatedRequest,
Pagination,
PaginationRequest,
Permission,
PermissionCheckInput,
PermissionCheckResult,
RemoveOrgMemberRequest,
ResendOTPRequest,
ResendVerifyEmailRequest,
ResetPasswordRequest,
ResponseTypes,
RevokeTokenRequest,
ScimEndpoint,
ScimEndpointRequest,
SessionQueryRequest,
SignUpRequest,
TestEndpointRequest,
TestEndpointResponse,
TokenType,
TrustedIssuer,
TrustedIssuerRequest,
TrustedIssuersResponse,
UpdateAccessRequest,
UpdateClientRequest,
UpdateEmailTemplateRequest,
UpdateOrganizationRequest,
UpdateOrgOIDCConnectionRequest,
UpdateOrgSAMLConnectionRequest,
UpdateProfileRequest,
UpdateTrustedIssuerRequest,
UpdateUserRequest,
UpdateWebhookRequest,
User,
Expand Down Expand Up @@ -99,6 +142,8 @@
"AuthorizerError",
"AuthorizerConnectionError",
"AddEmailTemplateRequest",
"AddOrgMemberRequest",
"AddTrustedIssuerRequest",
"AddWebhookRequest",
"AdminLoginRequest",
"AdminMeta",
Expand All @@ -108,6 +153,17 @@
"AuthToken",
"CheckPermissionsRequest",
"CheckPermissionsResponse",
"Client",
"CLIENT_ASSERTION_TYPE_JWT_BEARER",
"ClientRequest",
"ClientsResponse",
"CreateClientRequest",
"CreateClientResponse",
"CreateOrganizationRequest",
"CreateOrgOIDCConnectionRequest",
"CreateOrgSAMLConnectionRequest",
"CreateScimEndpointRequest",
"CreateScimEndpointResponse",
"DeleteEmailTemplateRequest",
"DeleteUserRequest",
"EmailTemplate",
Expand All @@ -131,35 +187,65 @@
"GetTokenRequest",
"GetTokenResponse",
"GetUserRequest",
"GRANT_TYPE_AUTHORIZATION_CODE",
"GRANT_TYPE_CLIENT_CREDENTIALS",
"GRANT_TYPE_REFRESH_TOKEN",
"GRANT_TYPE_TOKEN_EXCHANGE",
"InviteMembersRequest",
"InviteMembersResponse",
"ListAuditLogRequest",
"ListClientsRequest",
"ListOrganizationsRequest",
"ListOrgMembersRequest",
"ListPermissionsRequest",
"ListPermissionsResponse",
"ListTrustedIssuersRequest",
"ListWebhookLogRequest",
"LoginRequest",
"MagicLinkLoginRequest",
"MetaData",
"OAuthProviders",
"Pagination",
"Organization",
"OrganizationRequest",
"OrganizationsResponse",
"OrgMember",
"OrgMembersResponse",
"OrgOIDCConnection",
"OrgOIDCConnectionRequest",
"OrgSAMLConnection",
"OrgSAMLConnectionRequest",
"PaginatedRequest",
"Pagination",
"PaginationRequest",
"Permission",
"PermissionCheckInput",
"PermissionCheckResult",
"RemoveOrgMemberRequest",
"ResendOTPRequest",
"ResendVerifyEmailRequest",
"ResetPasswordRequest",
"ResponseTypes",
"RevokeTokenRequest",
"ScimEndpoint",
"ScimEndpointRequest",
"SessionQueryRequest",
"SignUpRequest",
"TestEndpointRequest",
"TestEndpointResponse",
"TOKEN_TYPE_ACCESS_TOKEN",
"TOKEN_TYPE_JWT",
"TokenType",
"TrustedIssuer",
"TrustedIssuerRequest",
"TrustedIssuersResponse",
"UpdateAccessRequest",
"UpdateClientRequest",
"UpdateEmailTemplateRequest",
"UpdateOrganizationRequest",
"UpdateOrgOIDCConnectionRequest",
"UpdateOrgSAMLConnectionRequest",
"UpdateProfileRequest",
"UpdateTrustedIssuerRequest",
"UpdateUserRequest",
"UpdateWebhookRequest",
"User",
Expand Down
42 changes: 42 additions & 0 deletions src/authorizer/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from urllib.parse import urlparse

from .exceptions import AuthorizerError
from .types import GRANT_TYPE_TOKEN_EXCHANGE

# Supported transport protocols. ``graphql`` is the default (100% backward
# compatible). ``rest`` maps to the public/admin proto google.api.http paths;
Expand Down Expand Up @@ -87,6 +88,47 @@ def build_oauth_request(
return RequestSpec("POST", f"{authorizer_url}{path}", headers, body)


# Optional /oauth/token parameters, sent only when set. Covers refresh_token,
# client_credentials (RFC 6749 §4.4), RFC 7523 client_assertion, and RFC 8693
# token exchange (+ RFC 8707 resource).
_TOKEN_OPTIONAL_PARAMS = (
"refresh_token",
"client_secret",
"scope",
"client_assertion",
"client_assertion_type",
"subject_token",
"subject_token_type",
"actor_token",
"actor_token_type",
"resource",
)

def build_token_body(client_id: str, req: Any) -> dict[str, str]:
"""Build the /oauth/token form body from a GetTokenRequest.

Only set parameters are sent. The body MUST be form-encoded on the wire:
the server reads the RFC 8707 ``resource`` parameter from the POST form
(to reject repeated values), so a JSON body would drop it.
"""
grant_type = req.grant_type or "authorization_code"
if grant_type == "refresh_token" and not (req.refresh_token and req.refresh_token.strip()):
raise ValueError("refresh_token is required for refresh_token grant")
if grant_type == GRANT_TYPE_TOKEN_EXCHANGE and not (
req.subject_token and req.subject_token.strip()
):
raise ValueError("subject_token is required for token exchange grant")
body: dict[str, str] = {"client_id": client_id, "grant_type": grant_type}
if grant_type == "authorization_code":
body["code"] = req.code or ""
body["code_verifier"] = req.code_verifier or ""
for key in _TOKEN_OPTIONAL_PARAMS:
value = getattr(req, key)
if value:
body[key] = value
return body


def prepare_http(
config: ClientConfig,
spec: Any,
Expand Down
67 changes: 67 additions & 0 deletions src/authorizer/_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,71 @@ class MethodSpec:
"admin_signup": MethodSpec(GQL_ONLY, q.ADMIN_SIGNUP, "_admin_signup"),
"update_env": MethodSpec(GQL_ONLY, q.ADMIN_UPDATE_ENV, "_update_env"),
"generate_jwt_keys": MethodSpec(GQL_ONLY, q.ADMIN_GENERATE_JWT_KEYS, "_generate_jwt_keys"),
# Machine-agent-identity ops. Orgs/SSO/SCIM are graphql-only on the server.
# Clients + trusted issuers DO have proto RPCs server-side, but the vendored
# stubs (src/authorizer/_grpc) predate them — rest/grpc stay off until the
# stubs are re-vendored, so all of these are graphql-only for now.
"create_client": MethodSpec(GQL_ONLY, q.ADMIN_CREATE_CLIENT, "_create_client"),
"update_client": MethodSpec(GQL_ONLY, q.ADMIN_UPDATE_CLIENT, "_update_client"),
"delete_client": MethodSpec(GQL_ONLY, q.ADMIN_DELETE_CLIENT, "_delete_client"),
"rotate_client_secret": MethodSpec(
GQL_ONLY, q.ADMIN_ROTATE_CLIENT_SECRET, "_rotate_client_secret"
),
"get_client": MethodSpec(GQL_ONLY, q.ADMIN_GET_CLIENT, "_client"),
"clients": MethodSpec(GQL_ONLY, q.ADMIN_CLIENTS, "_clients"),
"add_trusted_issuer": MethodSpec(GQL_ONLY, q.ADMIN_ADD_TRUSTED_ISSUER, "_add_trusted_issuer"),
"update_trusted_issuer": MethodSpec(
GQL_ONLY, q.ADMIN_UPDATE_TRUSTED_ISSUER, "_update_trusted_issuer"
),
"delete_trusted_issuer": MethodSpec(
GQL_ONLY, q.ADMIN_DELETE_TRUSTED_ISSUER, "_delete_trusted_issuer"
),
"get_trusted_issuer": MethodSpec(GQL_ONLY, q.ADMIN_GET_TRUSTED_ISSUER, "_trusted_issuer"),
"trusted_issuers": MethodSpec(GQL_ONLY, q.ADMIN_TRUSTED_ISSUERS, "_trusted_issuers"),
"create_organization": MethodSpec(
GQL_ONLY, q.ADMIN_CREATE_ORGANIZATION, "_create_organization"
),
"update_organization": MethodSpec(
GQL_ONLY, q.ADMIN_UPDATE_ORGANIZATION, "_update_organization"
),
"delete_organization": MethodSpec(
GQL_ONLY, q.ADMIN_DELETE_ORGANIZATION, "_delete_organization"
),
"add_org_member": MethodSpec(GQL_ONLY, q.ADMIN_ADD_ORG_MEMBER, "_add_org_member"),
"remove_org_member": MethodSpec(GQL_ONLY, q.ADMIN_REMOVE_ORG_MEMBER, "_remove_org_member"),
"get_organization": MethodSpec(GQL_ONLY, q.ADMIN_GET_ORGANIZATION, "_organization"),
"organizations": MethodSpec(GQL_ONLY, q.ADMIN_ORGANIZATIONS, "_organizations"),
"org_members": MethodSpec(GQL_ONLY, q.ADMIN_ORG_MEMBERS, "_org_members"),
"create_org_oidc_connection": MethodSpec(
GQL_ONLY, q.ADMIN_CREATE_ORG_OIDC_CONNECTION, "_create_org_oidc_connection"
),
"update_org_oidc_connection": MethodSpec(
GQL_ONLY, q.ADMIN_UPDATE_ORG_OIDC_CONNECTION, "_update_org_oidc_connection"
),
"delete_org_oidc_connection": MethodSpec(
GQL_ONLY, q.ADMIN_DELETE_ORG_OIDC_CONNECTION, "_delete_org_oidc_connection"
),
"get_org_oidc_connection": MethodSpec(
GQL_ONLY, q.ADMIN_GET_ORG_OIDC_CONNECTION, "_org_oidc_connection"
),
"create_org_saml_connection": MethodSpec(
GQL_ONLY, q.ADMIN_CREATE_ORG_SAML_CONNECTION, "_create_org_saml_connection"
),
"update_org_saml_connection": MethodSpec(
GQL_ONLY, q.ADMIN_UPDATE_ORG_SAML_CONNECTION, "_update_org_saml_connection"
),
"delete_org_saml_connection": MethodSpec(
GQL_ONLY, q.ADMIN_DELETE_ORG_SAML_CONNECTION, "_delete_org_saml_connection"
),
"get_org_saml_connection": MethodSpec(
GQL_ONLY, q.ADMIN_GET_ORG_SAML_CONNECTION, "_org_saml_connection"
),
"create_scim_endpoint": MethodSpec(
GQL_ONLY, q.ADMIN_CREATE_SCIM_ENDPOINT, "_create_scim_endpoint"
),
"rotate_scim_token": MethodSpec(GQL_ONLY, q.ADMIN_ROTATE_SCIM_TOKEN, "_rotate_scim_token"),
"delete_scim_endpoint": MethodSpec(
GQL_ONLY, q.ADMIN_DELETE_SCIM_ENDPOINT, "_delete_scim_endpoint"
),
"get_scim_endpoint": MethodSpec(GQL_ONLY, q.ADMIN_GET_SCIM_ENDPOINT, "_scim_endpoint"),
}
Loading
Loading