Bug 958861

Summary: Support passing auth information without having to use HTTP Authorization header
Product: [Retired] oVirt Reporter: Vojtech Szocs <vszocs>
Component: ovirt-engine-apiAssignee: Michael Pasternak <mpastern>
Status: CLOSED NOTABUG QA Contact:
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: unspecifiedCC: acathrow, alonbl, bazulay, ecohen, eedri, iheim, jkt, mpastern, vszocs
Target Milestone: ---   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard: infra
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
: 958874 (view as bug list) Environment:
Last Closed: 2013-09-30 12:58:11 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 958874    

Description Vojtech Szocs 2013-05-02 14:40:06 UTC
The purpose of this RFE is to improve REST API integration in web applications (running JavaScript in web browser).

Unlike traditional HTTP clients, web applications don't have full control over HTTP request/response processing. Instead, the web browser is the "proxy" between JavaScript trying to send/receive requests, and the actual web server that serves these requests. Web browsers typically implement some standard conventions and behaviors on top of request/response processing, one of them is HTTP Authorization header behavior described below.

Since REST API expects (HTTP Basic) auth information to be passed using HTTP Authorization header, web application trying to integrate with REST API must set this header. However, after setting HTTP Authorization header by web application (i.e. via XmlHttpRequest) for the first request, the browser *remembers* this header and always sends it for subsequent requests, until the browser window is closed. Because of this, implementing logout functionality in web application is really hard when using HTTP Authorization header.

Furthermore, web application cannot take advantage of REST API session mechanism [http://wiki.ovirt.org/Features/RESTSessionManagement] because this mechanism expects HTTP Authorization header to be sent by client only for the *first* (create session) request, which is impossible due to browser-specific HTTP Authorization header handling as mentioned above.

To summarize, web application doesn't have full control over HTTP Authorization header, so REST API should provide some alternative to pass auth information.

Proposed solution:
Client (web application) can use "Prefer: custom-auth-header" to indicate preference of using custom HTTP header instead of standard HTTP Authorization header.

For example:

    GET /api HTTP/1.1
    Host: ovirt-engine
    Prefer: custom-auth-header
    Custom-Authorization: xxx

Example that works with REST API session mechanism (create session):

    GET /api HTTP/1.1
    Host: ovirt-engine
    Prefer: persistent-auth,custom-auth-header
    Custom-Authorization: xxx

Comment 1 Einav Cohen 2013-05-02 15:10:27 UTC
adding reference to the first e-mail in the related engine-devel thread:
http://lists.ovirt.org/pipermail/engine-devel/2013-April/004235.html

Comment 2 Vojtech Szocs 2013-05-14 14:21:18 UTC
@Michael: is there some RFE/document for your "URI based authentication" proposal?

Assuming user credentials (also possibly JSESSIONID value) could be passed via URI, we could use this as a workaround to avoid dealing with HTTP 'Authorization' header entirely (also possibly to avoid dealing with cookies entirely).

In other words, with "URI based authentication" implemented, we could just skip the 'Custom-Authorization' header entirely (originally proposed for this RFE).

Comment 3 Michael Pasternak 2013-09-15 13:36:41 UTC
AFAICS, fixing Bug 1007444 solves the issue mentioned here?!, i.e


all you need for session re-initation take place (when old session is expires),
is to keep authorization header in request (what is happens anyway in browsers),
i.e existence of the authorization header in request, does not turn off
session based authentication any more.


[1] https://bugzilla.redhat.com/show_bug.cgi?id=1007444#c1

Comment 4 Vojtech Szocs 2013-09-26 12:23:47 UTC
This is great news, thanks Michael!

If I understand correctly, when client sends request like this:

  Pass "Authorization" header
  Pass "Prefer:persistent-auth" header
  Pass "Cookie" header with JSESSIONID

Then it should behave like this:

  * if JSESSIONID maps to live (active) session, i.e. `SessionUtils.getCurrentSession(false) != null`, this session won't be re-created (JSESSIONID value stays the same!) but just validated during request processing
  * if JSESSIONID maps to dead (inactive) session, i.e. `SessionUtils.getCurrentSession(false) == null`, new session will be re-created during request processing

If I read patch [http://gerrit.ovirt.org/#/c/19244/] correctly, "Prefer:persistent-auth" header must be specified in order to attempt to re-use (validate) existing session. This makes much more sense than condition `... && !hasAuthorizationHeader` in Challenger.preProcess method. Session functionality should be tied to "Prefer:persistent-auth" header anyway, since "Authorization" header serves a conceptually different purpose and isn't really related to session functionality.

So the JavaScript client can now just send requests like shown above and extract JSESSIONID either from cookie or "JSESSIONID" response header (the JSESSIONID value might change due to re-creation of dead session) without fear of causing unintentional session re-creation (in case of live session).

Michael is right, if my understanding is correct, it would solve this BZ. Michael, can you please confirm my notes above? (thanks!)

Comment 5 Michael Pasternak 2013-09-29 10:04:14 UTC
(In reply to vszocs from comment #4)
> This is great news, thanks Michael!
> 
> If I understand correctly, when client sends request like this:
> 
>   Pass "Authorization" header
>   Pass "Prefer:persistent-auth" header
>   Pass "Cookie" header with JSESSIONID
> 
> Then it should behave like this:
> 
>   * if JSESSIONID maps to live (active) session, i.e.
> `SessionUtils.getCurrentSession(false) != null`, this session won't be
> re-created (JSESSIONID value stays the same!) but just validated during
> request processing
>   * if JSESSIONID maps to dead (inactive) session, i.e.
> `SessionUtils.getCurrentSession(false) == null`, new session will be
> re-created during request processing
> 
> If I read patch [http://gerrit.ovirt.org/#/c/19244/] correctly,
> "Prefer:persistent-auth" header must be specified in order to attempt to
> re-use (validate) existing session. This makes much more sense than
> condition `... && !hasAuthorizationHeader` in Challenger.preProcess method.
> Session functionality should be tied to "Prefer:persistent-auth" header
> anyway, since "Authorization" header serves a conceptually different purpose
> and isn't really related to session functionality.
> 
> So the JavaScript client can now just send requests like shown above and
> extract JSESSIONID either from cookie or "JSESSIONID" response header (the
> JSESSIONID value might change due to re-creation of dead session) without
> fear of causing unintentional session re-creation (in case of live session).
> 
> Michael is right, if my understanding is correct, it would solve this BZ.
> Michael, can you please confirm my notes above? (thanks!)

+1

Comment 6 Vojtech Szocs 2013-09-30 12:31:33 UTC
Thanks Michael, I think we can close this BZ now.

Auth info can still be passed via HTTP Authorization header. If the client doesn't have full control over HTTP Authorization header, it can still pass "Prefer:persistent-auth" + "Cookie" header to reuse the session (if it's still active), without fear of causing session re-creation (if the session is still active).

Comment 7 Vojtech Szocs 2013-09-30 12:46:27 UTC
As for passing JSESSIONID value from client to server alongside each request, for a JavaScript-based application running in web browser, the browser should handle the "Cookie" header automatically, as soon as "Set-Cookie" header was detected in a response from server.

So for JavaScript vs. REST API integration:

1. webapp makes initial request ("Authorization" + "Prefer:persistent-auth") and extracts JSESSIONID value from "JSESSIONID" response header (Note: cannot read cookie value due to cookie being set for different path, i.e. "/webadmin" vs. "/api")

2. webapp makes subsequent requests ("Authorization" + "Prefer:persistent-auth") and relies on browser to include "Cookie" header in each request (Note: initial response contained "Set-Cookie" header so browser will send "Cookie" header for each request from now on)

So webapp just needs to check response after each REST API call to see if there's new JSESSIONID value. If the response has "JSESSIONID" header, webapp will use this value if it needs to pass session ID to other systems.