Short answer: use a server-controlled session for the property-management portal after Google or GitHub sign-in, and use public-key verification only at API boundaries that need to validate signed credentials independently.
At 02:13, the page that matters is an alert, not a login screen. The on-call sees a spike in failed callbacks from leasing staff, while requests from already signed-in operators continue to load buildings and work orders. A single 401 counter cannot say whether the browser transaction was not bound, the session store is slow, or a signed credential failed its policy. The first operational decision is therefore to separate those stages before choosing a component.
The alert should have fired earlier on a stage-specific signal: callback validation, session creation, session lookup, and token verification each get an outcome counter with bounded reason labels. That instrumentation makes the incident actionable without putting emails, tenant IDs, tokens, or session IDs in logs. It also reveals the price of a noisy threshold: page on every rejected credential and responders learn to ignore the signal; page only after a sustained authentication-SLO breach and a small property portfolio may wait too long.
What should a developer portal do with sessions and public-key verification?
These mechanisms answer different questions. A session gives the portal current server-side control over logout, idle expiry, organization membership, and a fresh check before a sensitive action. Public-key verification proves that a signed token was produced by an allowed issuer and key; it does not, by itself, authorize a property manager to view a tenant record. The verifier still needs an explicit issuer, audience, algorithm, and validity-window policy.
Signatures do not revoke access.
For a Google or GitHub callback, bind the response to the browser transaction that started it, then map the external identity to a local user and organization. Rotate the local session identifier after authentication and after a privilege change. Keep the API verifier separate from that browser record so a disabled contractor is not granted access by an old external assertion. OWASP recommends reauthentication after high-risk events and renewal of session identifiers after privilege-level changes.
Capacity planning belongs in this decision. Estimate peak session reads per second during a provider-driven login burst, the maximum acceptable delay between a role removal and enforcement, and the storage failover time that still fits the SLO. Stateless credentials reduce a lookup on each API request, but immediate revocation and policy changes become harder unless short lifetimes or a deny list add state back. There is no universal winner.
When does the first failed callback become an incident?
Work backward from the alert. If session_lookup remains healthy while callback_state rejects rise, the failure is before local session use. If verification rejects an unexpected issuer or audience, cryptography may be healthy and policy may be correctly refusing the credential. Keep the external response generic; expose a bounded internal reason only to the metrics and traces used by responders.
One short line matters.
package auth
import (
"context"
"errors"
"net/http"
)
type Claims struct {
Subject string
Issuer string
}
type Verifier interface {
Verify(ctx context.Context, encoded string) (Claims, error)
}
type Counter interface {
Add(stage, outcome, reason string)
}
type Handler struct {
Verifier Verifier
Checks Counter
}
func (h Handler) AuthenticateAPI(r *http.Request, encoded string) (Claims, error) {
claims, err := h.Verifier.Verify(r.Context(), encoded)
if err != nil {
h.Checks.Add("token_verification", "reject", classify(err))
return Claims{}, errors.New("authentication failed")
}
h.Checks.Add("token_verification", "accept", "none")
return claims, nil
}
func classify(err error) string {
// Keep labels bounded; never use token or user data as a label.
return "policy_or_signature"
}
The maintained cryptographic library behind Verifier should distinguish expired, issuer, audience, key-selection, signature, and malformed-input outcomes when it can do so reliably. I am not sure every portal benefits from all six labels; a short load test and a staged key rotation will show whether the split shortens diagnosis or only enlarges dashboards.
How can Google and GitHub sign-in survive rotation and recovery?
Treat rotation as a tested transition, not a calendar task. During a planned signing-key change, accept old and new keys only for the declared overlap window, record the policy revision with each decision, and verify that rollback does not restore an obsolete allowlist or extend a session lifetime. For social callbacks, run parallel transactions and confirm that one browser cannot complete another browser's flow; replayed state, arbitrary redirect targets, expired credentials, and tokens for another audience belong in the same test suite.
Recovery is where friction and security meet. Removing a user's organization role should take effect within the stated enforcement window, while a provider outage should not delete every valid local session. Require a fresh authentication step for credential rotation or tenant-data export, and make the step visible in audit records. A deployment canary should compare accept and rejection rates by stage before the new policy becomes the default.
Buy versus build under an on-call budget
The useful comparison is ownership under failure, not a feature checklist.
| Responsibility | Managed component | Self-hosted component | Pressure to resolve |
|---|---|---|---|
| Provider protocol changes | More upkeep is handled externally | Platform team tracks and ships changes | Release cadence and on-call load |
| Session and revocation state | Fewer servers to operate, with policy constraints | Direct control plus storage and failover duty | Enforcement window and data boundary |
| Signing-key lifecycle | Some controls arrive integrated | Team owns generation, protection, publication, and drills | Audit evidence and rotation readiness |
| Exit path | Migration depends on export and interfaces | Portability depends on internal coupling | Lock-in and staff time |
The catch is fit. A managed component is not suitable when its revocation window, data boundary, or audit evidence misses the portal's SLO. A self-hosted component is not suitable when the platform team cannot staff upgrades, backups, key ceremonies, and the 02:13 page. Stick with the option whose failure modes can be exercised in staging; a longer capability list does not compensate for an unowned recovery path.
Tune the page, then measure the friction
Separate expected denials from system failures. Expired or malformed credentials belong in a security and product signal; an unavailable callback dependency or session store belongs in an availability signal. Page on a sustained condition that threatens the authentication SLO, and send low-volume policy anomalies to a dashboard or ticket until they show user impact. Review the smallest property portfolios as well as weekday leasing peaks, because your mileage may vary.
The final check is concrete: can on-call identify the failing stage, can the team revoke access within the promised window, and can a user recover without bypassing the transaction binding? If any answer is no, adding another login provider increases surface area without improving access.
References
- https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- https://datatracker.ietf.org/doc/html/rfc6749
- https://datatracker.ietf.org/doc/html/rfc7517
Top comments (0)