syntax = "proto3";

package nominal.authorization.internal.v1;

import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "nominal/protos/gen/v1/alias.proto";
import "nominal/protos/gen/v1/error.proto";

// Cluster-internal endpoint that exchanges a zero-trust-proxy-injected JWT
// for a Nominal-issued access token. The intended caller is a scout-side
// Jersey filter / gRPC interceptor that runs in proxy-fronted deployments,
// sees a trusted JWT in a known header (e.g. X-Goog-IAP-JWT-Assertion for
// Google IAP; whatever header the deployment's proxy uses otherwise), and
// needs to synthesize a Nominal Authorization for downstream filters.
//
// The exchange chain is: verify-proxy-JWT (dispatched to the matching
// customJwtProviders entry by iss) -> resolve email to single Nominal org ->
// createOrUpdateUserByEmailAndSyncOrgs -> issue Nominal JWT.
//
// Modeled as a Get because the caller is expected to cache the exchange
// result keyed by the encoded proxy JWT — we do not want to mint a new
// Nominal token on every API request the caller fronts. The proxy JWT is
// URL-safe base64, so it is safe to embed in a GET path.
//
// This service must not be exposed via the public ingress.

message AccessToken {
  // JWT bearer token suitable for use as a Nominal Authorization header value.
  string token = 1;
  // Unix seconds at which the token expires.
  int64 expires_at_seconds = 2;
  // RID of the Nominal user the token authenticates.
  string user_rid = 3 [(nominal.gen.v1.alias) = {java_name: "io.nominal.authentication.api.UserRid"}];
  // RID of the Nominal org the token authenticates the user into.
  string org_rid = 4 [(nominal.gen.v1.alias) = {java_name: "io.nominal.authentication.api.OrgRid"}];
}

message GetAccessTokenFromProxyJwtRequest {
  // Raw JWT value forwarded by a zero-trust proxy. Not yet trusted by the
  // caller; gatekeeper performs full verification (signature, iss, aud, exp)
  // against the matching customJwtProviders entry before minting a token.
  string proxy_jwt = 1 [(buf.validate.field).string.min_len = 1];
}

message GetAccessTokenFromProxyJwtResponse {
  AccessToken access_token = 1;
}

// buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX
enum InternalProxyJwtError {
  // The endpoint cannot mint a Nominal access token from a proxy-injected
  // JWT — for any of: no customJwtProviders entry matches the JWT's iss;
  // the JWT fails verification; the verified email does not resolve to a
  // single Nominal user/org. The cases are intentionally indistinguishable
  // to callers to avoid leaking which proxy issuers are configured on this
  // deployment.
  INTERNAL_PROXY_JWT_ERROR_PROXY_JWT_UNAVAILABLE = 0 [(nominal.gen.v1.error) = {
    code: "PERMISSION_DENIED"
    message: "ProxyJwtUnavailable"
  }];
}

// Cluster-internal exchange of a proxy-injected JWT for a Nominal access token.
// Must not be exposed via the public ingress.
service InternalProxyJwtService {
  // Verify a proxy-injected JWT and return a Nominal access token for the
  // verified email. The Nominal token's expiry is bounded by the proxy
  // JWT's expiry, capped at the standard Nominal session maximum.
  rpc GetAccessTokenFromProxyJwt(GetAccessTokenFromProxyJwtRequest) returns (GetAccessTokenFromProxyJwtResponse) {
    option (google.api.http) = {get: "/authorization/internal/v1/proxy-jwt/{proxy_jwt}/access-token"};
  }
}
