handleAgentIdentity is POST /agent/identity. In Slice 5b-2 it serves the bootstrap: authenticate the caller's agent-scoped credential and mint an identity_assertion for that one agent. (anonymous/ID-JAG registration types are later sub-slices; an unknown/auth-less request is rejected, not silently s
(w http.ResponseWriter, r *http.Request)
| 56 | // are later sub-slices; an unknown/auth-less request is rejected, not |
| 57 | // silently self-provisioned.) |
| 58 | func (a *API) handleAgentIdentity(w http.ResponseWriter, r *http.Request) { |
| 59 | if !a.agentAuthReady() { |
| 60 | writeOAuthError(w, http.StatusNotImplemented, "not_implemented", |
| 61 | "agent identity is not enabled on this deployment") |
| 62 | return |
| 63 | } |
| 64 | // Bootstrap auth: the agent presents its e2a_agt_ key (Slice 5a). The |
| 65 | // credential must be agent-scoped and bound to a specific agent — an |
| 66 | // account-scoped key has no single agent to assert for. |
| 67 | p, err := a.authenticatePrincipal(r) |
| 68 | if err != nil { |
| 69 | a.writeAuthError(w, r, err) |
| 70 | return |
| 71 | } |
| 72 | if p.Scope != identity.ScopeAgent || p.AgentID == "" { |
| 73 | writeOAuthError(w, http.StatusForbidden, "forbidden", |
| 74 | "agent identity requires an agent-scoped credential bound to one agent") |
| 75 | return |
| 76 | } |
| 77 | |
| 78 | // Load the agent for its current assertion_version (the kill-switch |
| 79 | // counter stamped into the token). |
| 80 | ag, err := a.store.GetAgentByID(r.Context(), p.AgentID) |
| 81 | if err != nil || ag == nil || ag.UserID != p.User.ID { |
| 82 | writeOAuthError(w, http.StatusForbidden, "forbidden", "agent not found") |
| 83 | return |
| 84 | } |
| 85 | |
| 86 | assertion, exp, err := a.signer.SignIdentityAssertion(ag.ID, identity.ScopeAgent, ag.AssertionVersion, a.agentAuthIssuer()) |
| 87 | if err != nil { |
| 88 | writeOAuthError(w, http.StatusInternalServerError, "server_error", "failed to mint identity assertion") |
| 89 | return |
| 90 | } |
| 91 | w.Header().Set("Content-Type", "application/json") |
| 92 | w.Header().Set("Cache-Control", "no-store") |
| 93 | _ = json.NewEncoder(w).Encode(identityAssertionResponse{ |
| 94 | IdentityAssertion: assertion, |
| 95 | TokenType: "N_A", // an assertion is presented at /oauth2/token, not used as a bearer |
| 96 | Subject: ag.ID, |
| 97 | ExpiresAt: exp.UTC().Format(time.RFC3339), |
| 98 | }) |
| 99 | } |
| 100 | |
| 101 | type accessTokenResponse struct { |
| 102 | AccessToken string `json:"access_token"` |
nothing calls this directly
no test coverage detected