syntax = "proto3";
package defguard.proxy.v2;

import "common/client_types.proto";
import "google/protobuf/empty.proto";
import "v2/common.proto";

// Password Reset (proxy-internal)

message PasswordResetInitializeRequest {
  string email = 1;
}

message PasswordResetStartRequest {
  string token = 1;
}

message PasswordResetStartResponse {
  int64 deadline_timestamp = 1;
}

message PasswordResetRequest {
  string password = 1;
  optional string token = 2;
}

message ClientMfaTokenValidationRequest {
  string token = 1;
}

message ClientMfaTokenValidationResponse {
  bool token_valid = 1;
}

message AuthInfoResponse {
  string url = 1;
  string csrf_token = 2;
  string nonce = 3;
  optional string button_display_name = 4;
}

message AuthCallbackRequest {
  string code = 1;
  string nonce = 2;
}

message AuthCallbackResponse {
  string url = 1;
  string token = 2;
}

message ClientMfaOidcAuthenticateRequest {
  string code = 1;
  string state = 2;
  string nonce = 3;
}

// Common client info
message DeviceInfo {
  string ip_address = 1;
  optional string user_agent = 2;
  optional string version = 3;
  optional string platform = 4;
}

message AwaitRemoteMfaFinishRequest {
  string token = 1;
}

message AwaitRemoteMfaFinishResponse {
  string preshared_key = 1;
}

message InitialInfo {
  bytes private_cookies_key = 1;
}

/*
 * Error response variant.
 * Due to reverse proxy -> core communication this is how we
 * can return gRPC errors from core.
 */
message CoreError {
  int32 status_code = 1;
  string message = 2;
}

/*
 * CoreResponse represents messages send from core to proxy
 * in response to CoreRequest.
 */
message CoreResponse {
  uint64 id = 1;
  oneof payload {
    google.protobuf.Empty empty = 2;
    defguard.client_types.EnrollmentStartResponse enrollment_start = 3;
    defguard.client_types.DeviceConfigResponse device_config = 4;
    PasswordResetStartResponse password_reset_start = 5;
    defguard.client_types.ClientMfaStartResponse client_mfa_start = 6;
    defguard.client_types.ClientMfaFinishResponse client_mfa_finish = 7;
    CoreError core_error = 8;
    defguard.client_types.InstanceInfoResponse instance_info = 9;
    AuthInfoResponse auth_info = 10;
    AuthCallbackResponse auth_callback = 11;
    ClientMfaTokenValidationResponse client_mfa_token_validation = 12;
    defguard.client_types.CodeMfaSetupStartResponse code_mfa_setup_start_response = 13;
    defguard.client_types.CodeMfaSetupFinishResponse code_mfa_setup_finish_response = 14;
    InitialInfo initial_info = 15;
    AwaitRemoteMfaFinishResponse await_remote_mfa_finish = 16;
    HttpsCerts https_certs = 17;
    google.protobuf.Empty clear_https_certs = 18;
  }
}

message HttpsCerts {
  string cert_pem = 1;
  string key_pem = 2;
}

/*
 * Sent from core to proxy to trigger ACME HTTP-01 certificate issuance.
 */
message AcmeChallenge {
  string domain = 1;
  // JSON-serialized instant-acme AccountCredentials; empty string means create a new account.
  string account_credentials_json = 2;
}

/*
 * Sent from proxy to core after ACME certificate issuance completes.
 */
message AcmeCertificate {
  string cert_pem = 1;
  string key_pem = 2;
  // JSON-serialized instant-acme AccountCredentials for reuse on renewal.
  string account_credentials_json = 3;
}

/*
 * Progress steps emitted by the proxy during ACME HTTP-01 certificate issuance.
 * Streamed as AcmeIssueEvent payloads before the final AcmeCertificate.
 */
enum AcmeStep {
  ACME_STEP_UNSPECIFIED = 0;
  ACME_STEP_CONNECTING = 1;
  ACME_STEP_VALIDATING_DOMAIN = 2;
  ACME_STEP_ISSUING_CERTIFICATE = 3;
  ACME_STEP_CHECKING_DOMAIN = 4;
}

message AcmeProgress {
  AcmeStep step = 1;
}

/*
 * Log lines collected by the proxy during ACME certificate issuance.
 * Sent once on failure, immediately before the gRPC error status.
 */
message AcmeLogs {
  repeated string lines = 1;
}

/*
 * Wrapper message streamed by IssueAcme.
 * Carries either a progress update, the final certificate, or (on failure)
 * the collected proxy log lines.
 */
message AcmeIssueEvent {
  oneof payload {
    AcmeProgress progress = 1;
    AcmeCertificate certificate = 2;
    AcmeLogs logs = 3;
  }
}

/*
 * CoreRequest represents messages send from proxy to core.
 */
message CoreRequest {
  uint64 id = 1;
  DeviceInfo device_info = 2;
  oneof payload {
    defguard.client_types.EnrollmentStartRequest enrollment_start = 3;
    defguard.client_types.ActivateUserRequest activate_user = 4;
    defguard.client_types.NewDevice new_device = 5;
    defguard.client_types.ExistingDevice existing_device = 6;
    PasswordResetInitializeRequest password_reset_init = 7;
    PasswordResetStartRequest password_reset_start = 8;
    PasswordResetRequest password_reset = 9;
    defguard.client_types.ClientMfaStartRequest client_mfa_start = 10;
    defguard.client_types.ClientMfaFinishRequest client_mfa_finish = 11;
    defguard.client_types.InstanceInfoRequest instance_info = 12;
    defguard.client_types.AuthInfoRequest auth_info = 13;
    AuthCallbackRequest auth_callback = 14;
    ClientMfaOidcAuthenticateRequest client_mfa_oidc_authenticate = 15;
    defguard.client_types.RegisterMobileAuthRequest register_mobile_auth = 16;
    ClientMfaTokenValidationRequest client_mfa_token_validation = 17;
    defguard.client_types.CodeMfaSetupStartRequest code_mfa_setup_start = 18;
    defguard.client_types.CodeMfaSetupFinishRequest code_mfa_setup_finish = 19;
    AwaitRemoteMfaFinishRequest await_remote_mfa_finish = 20;
    AcmeCertificate acme_certificate = 21;
  }
}

service Proxy {
  /*
   * Bi-directional communication between core and proxy.
   * For security reasons, the connection has to be initiated by core,
   * so requests and responses are actually sent in reverse.
   */
  rpc Bidi(stream CoreResponse) returns (stream CoreRequest);
  /*
   * Purges existing on-disk gRPC TLS credentials and signals the server
   * to re-enter setup mode so that core can provision new ones.
   */
  rpc Purge(google.protobuf.Empty) returns (google.protobuf.Empty);
  /*
   * Triggers ACME HTTP-01 certificate issuance directly on the proxy.
   * Streams progress events followed by the issued certificate.
   */
  rpc TriggerAcme(AcmeChallenge) returns (stream AcmeIssueEvent);
}

// Service used for initial Proxy setup, for configuring TLS certificate
// on Proxy for gRPC communication.
service ProxySetup {
  rpc Start(google.protobuf.Empty) returns (stream defguard.common.v2.LogEntry);
  rpc GetCsr(defguard.common.v2.CertificateInfo) returns (defguard.common.v2.DerPayload);
  rpc SendCert(defguard.common.v2.CertBundle) returns (google.protobuf.Empty);
}
