Skip to main content
Version: Next

gRPC Authority APIs

Nauthilus exposes typed protobuf APIs on the authority listener. The same listener can serve two use cases:

  • external backchannel clients that call nauthilus.auth.v1.AuthService;
  • Nauthilus edge instances that use nauthilus.auth.v1.AuthService and nauthilus.identity.v1.IdentityBackendService as a remote authority backend.

The AuthService reuses the same authentication pipeline as /api/v1/auth/json and /api/v1/auth/cbor: backend selection, policy evaluation, brute-force handling, Lua subject sources, post-actions, metrics, logging, cache behavior, and list-account semantics stay aligned.

The identity backend service is an internal authority surface for split edge/authority deployments. Browser clients and ordinary service clients should not call it directly.

Listener Configuration

runtime:
servers:
grpc:
authority:
enabled: true
address: "127.0.0.1:9444"
tls:
enabled: false
cert: ""
key: ""
client_ca: ""
min_tls_version: "TLS1.2"
require_client_cert: false

The default address is loopback-only. Plaintext gRPC is only accepted on loopback addresses. Use TLS and usually mTLS before exposing the listener to another host or container network.

min_tls_version accepts TLS1.2 and TLS1.3; unset values default to TLS1.2. The gRPC listener always advertises HTTP/2 through ALPN and does not expose configurable cipher suites.

Timeouts are shared with HTTP authentication:

runtime:
timeouts:
redis_read: 1s
redis_write: 2s
ldap_search: 3s
ldap_bind: 3s
ldap_modify: 5s
lua_backend: 5s
lua_script: 30s

Caller Authentication

The gRPC listener uses the backchannel caller-auth configuration:

auth:
backchannel:
basic_auth:
enabled: true
username: "backchannel"
password: "change-me"
oidc_bearer:
enabled: true

Clients send credentials through gRPC metadata:

authorization: Basic <base64(username:password)>
authorization: Bearer <access-token>

Caller-auth failures are transport errors such as Unauthenticated or PermissionDenied. User authentication failures are domain decisions inside successful RPC responses.

Policy-localized status messages use gRPC metadata. A client can send language preference metadata:

accept-language: de-DE,de;q=0.9,en;q=0.8

If an auth policy selected response_message.from: i18n, Nauthilus resolves the key at the gRPC response boundary. A policy-selected response_language takes precedence over incoming accept-language. The response field status_message carries the rendered text, and Nauthilus sends response metadata when localization selected a language:

content-language: de

AuthService

package nauthilus.auth.v1;

service AuthService {
rpc Authenticate(AuthRequest) returns (AuthResponse);
rpc LookupIdentity(LookupIdentityRequest) returns (AuthResponse);
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse);
}

Authenticate

Authenticate mirrors the JSON and CBOR auth request model. username and password are required for normal password authentication. Optional request fields carry the same protocol, client, TLS, OIDC, and login-attempt metadata as the REST request fields.

Authentication failures return decision=AUTH_DECISION_FAIL with ok=false. Temporary backend or policy failures return decision=AUTH_DECISION_TEMPFAIL. Unexpected server errors are reported as gRPC transport errors.

LookupIdentity

LookupIdentity resolves identity data without a password check. It is used by flows that already have a trusted session or need account and attribute materialization after another authority operation. In split edge/authority deployments it lets the edge resolve user attributes without local LDAP or Lua backend access.

ListAccounts

ListAccounts is the gRPC equivalent of mode=list-accounts. It does not emulate HTTP query parameters.

AuthResponse

enum AuthDecision {
AUTH_DECISION_UNSPECIFIED = 0;
AUTH_DECISION_OK = 1;
AUTH_DECISION_FAIL = 2;
AUTH_DECISION_TEMPFAIL = 3;
}

message AuthResponse {
bool ok = 1;
AuthDecision decision = 2;
string session = 3;
string account_field = 4;
string totp_secret_field = 5;
uint32 backend = 6;
map<string, nauthilus.common.v1.AttributeValues> attributes = 7;
string status_message = 8;
string error = 9;
nauthilus.common.v1.BackendRef backend_ref = 10;
}

backend_ref is present when the authority wants the caller to continue later operations against the same authority-side backend selection. The edge stores it as an opaque handle and sends it back for MFA, WebAuthn, and identity follow-up operations.

ListAccountsResponse

message ListAccountsResponse {
repeated string accounts = 1;
string session = 2;
}

IdentityBackendService

package nauthilus.identity.v1;

service IdentityBackendService {
rpc ResolveUser(ResolveUserRequest) returns (UserSnapshotResponse);
rpc GetMFAState(GetMFAStateRequest) returns (MFAStateResponse);
rpc BeginTOTPRegistration(BeginTOTPRegistrationRequest) returns (BeginTOTPRegistrationResponse);
rpc FinishTOTPRegistration(FinishTOTPRegistrationRequest) returns (MFAWriteResponse);
rpc VerifyTOTP(VerifyTOTPRequest) returns (VerifyTOTPResponse);
rpc DeleteTOTP(DeleteTOTPRequest) returns (MFAWriteResponse);
rpc GenerateRecoveryCodes(GenerateRecoveryCodesRequest) returns (GenerateRecoveryCodesResponse);
rpc UseRecoveryCode(UseRecoveryCodeRequest) returns (UseRecoveryCodeResponse);
rpc DeleteRecoveryCodes(DeleteRecoveryCodesRequest) returns (MFAWriteResponse);
rpc GetWebAuthnCredentials(GetWebAuthnCredentialsRequest) returns (WebAuthnCredentialsResponse);
rpc SaveWebAuthnCredential(SaveWebAuthnCredentialRequest) returns (MFAWriteResponse);
rpc UpdateWebAuthnCredential(UpdateWebAuthnCredentialRequest) returns (MFAWriteResponse);
rpc DeleteWebAuthnCredential(DeleteWebAuthnCredentialRequest) returns (MFAWriteResponse);
}

This service exposes persistent identity and MFA data operations to trusted edge instances. It never returns TOTP secrets except during a fresh registration response, and it returns recovery codes only once when a new set is generated.

RPCPurpose
ResolveUserResolve released identity data, groups, attributes, MFA state, and an updated backend reference.
GetMFAStateRead public MFA state and optional public WebAuthn credential descriptors.
BeginTOTPRegistrationCreate pending TOTP setup material with an idempotency key.
FinishTOTPRegistrationVerify the setup code and persist the TOTP secret on the authority side.
VerifyTOTPVerify a TOTP code against authority-owned state.
DeleteTOTPRemove TOTP state.
GenerateRecoveryCodesGenerate and persist a fresh recovery-code set.
UseRecoveryCodeConsume one recovery code and return the remaining count.
DeleteRecoveryCodesRemove all recovery codes for the user.
GetWebAuthnCredentialsRead public WebAuthn credentials for ceremony setup.
SaveWebAuthnCredentialPersist a new WebAuthn credential after registration.
UpdateWebAuthnCredentialUpdate a credential, including sign-count changes after login.
DeleteWebAuthnCredentialDelete a credential by credential id.

Request Context

Identity backend requests include a RequestContext. It carries transport-neutral metadata from the edge:

  • username and protocol metadata;
  • client IP, port, hostname, and user-agent fields;
  • TLS client certificate fields when available;
  • OIDC client id and SAML entity id;
  • login-attempt counter;
  • arbitrary safe metadata attributes;
  • edge_instance, edge_request_id, and requested_language.

The authority uses this context for logging, policy decisions, backend-reference validation, and localized safe messages.

User Snapshot

ResolveUser returns a UserSnapshot:

FieldMeaning
usernameResolved username.
accountAccount value selected by the authority.
unique_user_idStable user id for IdP subject materialization.
display_nameReleased display name.
attributesReleased attributes requested by the edge and allowed by policy.
groupsReleased group names.
group_dnsReleased group DNs.
backendUpdated backend reference.
mfaPublic MFA state.

The AttributeRequest inside ResolveUserRequest lets the edge request specific attributes, standard identity fields, group names, group DNs, and missing-attribute diagnostics.

MFA State

MFAState contains public state only:

FieldMeaning
has_totpWhether a TOTP secret exists.
recovery_code_countNumber of remaining recovery codes.
has_webauthnWhether at least one WebAuthn credential exists.
webauthn_credentialsPublic WebAuthn credential descriptors.
preferred_methodAuthority-selected preferred MFA method.

WebAuthnCredential contains credential id, public key, sign count, transports, backup flags, attestation type, display name, last-used timestamp, and raw JSON. It does not contain private key material.

Backend References

message BackendRef {
string type = 1;
string name = 2;
string protocol = 3;
string authority = 4;
string opaque_token = 5;
}

The opaque token is a handle to authority Redis state. It is not a serialized backend credential. The authority validates it on every follow-up operation.

Backend references bind to the authority-side backend, username, operation family, caller identity, and edge metadata. Missing, expired, malformed, or wrong-purpose references fail closed. Edges must not reconstruct backend state locally.

Operation Status

Identity backend responses use a safe domain status:

enum OperationResult {
OPERATION_RESULT_UNSPECIFIED = 0;
OPERATION_RESULT_OK = 1;
OPERATION_RESULT_FAIL = 2;
OPERATION_RESULT_TEMPFAIL = 3;
OPERATION_RESULT_DENIED = 4;
OPERATION_RESULT_NOT_FOUND = 5;
OPERATION_RESULT_CONFLICT = 6;
}

message OperationStatus {
OperationResult result = 1;
string error_code = 2;
string safe_message = 3;
string edge_request_id = 4;
string authority_request_id = 5;
repeated ErrorDetail details = 6;
}

Transport errors mean the RPC itself could not be accepted or completed. OperationStatus describes an accepted authority operation and is safe to expose to logs or user-facing error mapping when appropriate.

Authority Scope Matrix

When caller auth uses OIDC bearer tokens, the authority checks scopes independently from the edge's local allowed_operations list.

ScopeTypical RPCs
nauthilus:authenticateAuthService.Authenticate
nauthilus:lookup_identityAuthService.LookupIdentity, IdentityBackendService.ResolveUser
nauthilus:list_accountsAuthService.ListAccounts
nauthilus:mfa_readGetMFAState, MFA state included in ResolveUser
nauthilus:mfa_verifyVerifyTOTP, UseRecoveryCode
nauthilus:mfa_writeBeginTOTPRegistration, FinishTOTPRegistration, DeleteTOTP, GenerateRecoveryCodes, DeleteRecoveryCodes
nauthilus:webauthn_readGetWebAuthnCredentials, WebAuthn credentials included in GetMFAState
nauthilus:webauthn_writeSaveWebAuthnCredential, UpdateWebAuthnCredential, DeleteWebAuthnCredential
nauthilus:attribute_readAttribute release through ResolveUser

Grant only the scopes the caller needs. For a split edge, keep the local remote backend allowed_operations list aligned with the authority scopes.

grpcurl Examples

Plaintext loopback listener with Basic backchannel authentication:

grpcurl \
-plaintext \
-H "authorization: Basic $(printf 'backchannel:change-me' | base64)" \
-d '{
"username": "alice@example.test",
"password": "secret",
"clientIp": "198.51.100.10",
"protocol": "imap",
"method": "plain",
"authLoginAttempt": 1
}' \
127.0.0.1:9444 \
nauthilus.auth.v1.AuthService/Authenticate

Lookup identity:

grpcurl \
-plaintext \
-H "authorization: Bearer ${ACCESS_TOKEN}" \
-d '{
"username": "alice@example.test",
"clientIp": "198.51.100.10",
"protocol": "oidc"
}' \
127.0.0.1:9444 \
nauthilus.auth.v1.AuthService/LookupIdentity

List accounts:

grpcurl \
-plaintext \
-H "authorization: Bearer ${ACCESS_TOKEN}" \
-d '{
"username": "alice@example.test",
"clientIp": "198.51.100.10",
"protocol": "account-provider"
}' \
127.0.0.1:9444 \
nauthilus.auth.v1.AuthService/ListAccounts