Over the last few days I have built a proof-of-concept demo using the GSS-API and SPNEGO. The aim is to give users single-sign-on access to services offered by our custom application server via Http RESTful web-services.
A user holding a valid Kerberos Ticket Granting Ticket (TGT) can call the SPNEGO enabled web-service, the Client and Server will negotiate, the user will be authenticated (both by Kerberos and on application level), and will (on successful authentication) have a Service Ticket for my Service Principal in his Ticket Cache.
This works well using CURL with the --negotiate flag as a test client.
On a first pass CURL makes a normal HTTP request with no special headers. This request is rejected by the Server, which adds "WWW-Authenticate: Negotiate" to the response headers, suggesting negotiation. CURL then gets a Service Ticket from the KDC, and makes a second request, this time with Negotiate + the wrapped Service Ticket in the request header (NegTokenInit) The Server then unwraps the ticket, authenticates the user, and (if authentication was successful) executes the requested service (e.g. login).
The question is, what should happen on subsequent service calls from the client to the server? The client now has a valid Kerberos Service Ticket, yet additional calls via CURL using SPNEGO makes the same two passes described above.
As I see it, I have a number of options:
1) Each service call repeats the full 2 pass SPNEGO negotiation (as CURL does). While this maybe the simplest option, at least in theory there will some overhead: both the client and the server are creating and tearing down GSS Contexts, and the request is being sent twice over the network, probably ok for GETs, less so for POSTs, as discusses in the questions below:
Why does the Authorization line change for every firefox request?
Why Firefox keeps negotiating kerberos service tickets?
But is the overhead* noticeable in real-life? I guess only performance testing will tell.
2) The first call uses SPNEGO negotiation. Once successfully authenticated, subsequent calls use application level authentication. This would seem to be the approach taken by Websphere Application Server, which uses Lightweight Third Party Authentication (LTPA) security tokens for subsequent calls.
Initially this seems to be a bit weird. Having successfully negotiated that Kerberos can be used, why fall back to something else? On the other hand, this approach might be valid if GSS-API / SPNEGO can be shown to cause noticeable overhead / performance loss.
3) The first call uses SPNEGO negotiation. Once successfully authenticated and trusted, subsequent calls use GSS-API Kerberos. In which case, I might as well do option 4) below.
4) Dump SPNEGO, use "pure" GSS-API Kerberos. I could exchange the Kerberos tickets via custom Http headers or cookies.
Is there a best practice?
As background: Both the client and server applications are under my control, both are implemented in java, and I know both "speak" Kerberos. I chose SPNEGO as "a place to start" for the proof-of-concept, and for worming my way into the world of Kerberos and Single Sign On, but it is not a hard requirement.
The proof-of-concept runs on OEL Linux servers with FreeIPA (because that is what I have in our dungeons), but the likely application will be Windows / Active Directory.
* or significant compared to other performance factors such as the database, use of XML vs JSON for the message bodies etc.
** If in the future we wanted to allow browser based access to the web services, then SPNEGO would be the way to go.