mr_roboto has asked for the wisdom of the Perl Monks concerning the following question:

I'm a fairly new monk, and still very much a Perl newbie, so this may be obvious, but I'm throwing it out anyway.

One of my current projects involves a site where I have a number of directorys that are restricted via Apache's BasicAuth, and I'm trying to create a form that will allow access to the sites in these directories without having the popup window come up for entering the Username and Password. What I want to know is, Is there a way to send a 401 header with the Username and Password in it with CGI.pm ?

I originally tried it with a redirect putting the username and password into the URL in the form 'http://username:password@host.domain.tld/directory/' , which worked fine when used with Netscape, but IE still brings up the Auth box. You can type the URL into the location bar in IE and hit enter and it works, which puzzles me.

Could any monks give me a hand here ?

Replies are listed 'Best First'.
Re: Passing a 401 header with CGI.pm ?
by swiftone (Curate) on Dec 02, 2000 at 02:08 UTC
    Okay, this is hardly complete, but it satisfies my research needs for the day.

    RFC 2617 spells out authentication protocols. I found RFC 2617 to have the following paragraph:

    2 Basic Authentication Scheme The "basic" authentication scheme is based on the model that the client must authenticate itself with a user-ID and a password for each realm. The realm value should be considered an opaque string which can only be compared for equality with other realms on that server. The server will service the request only if it can validate the user-ID and password for the protection space of the Request-URI. There are no optional authentication parameters. For Basic, the framework above is utilized as follows: challenge = "Basic" realm credentials = "Basic" basic-credentials Upon receipt of an unauthorized request for a URI within the protection space, the origin server MAY respond with a challenge like the following: WWW-Authenticate: Basic realm="WallyWorld" where "WallyWorld" is the string assigned by the server to identify the protection space of the Request-URI. A proxy may respond with the same challenge using the Proxy-Authenticate header field. To receive authorization, the client sends the userid and password, separated by a single colon (":") character, within a base64 [7] encoded string in the credentials. basic-credentials = base64-user-pass base64-user-pass = <base64 [4] encoding of user-pass, except not limited to 76 char/line> user-pass = userid ":" password userid = *<TEXT excluding ":"> password = *TEXT Userids might be case sensitive. If the user agent wishes to send the userid "Aladdin" and password "open sesame", it would use the following header field: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== A client SHOULD assume that all paths at or deeper than the depth of the last symbolic element in the path field of the Request-URI also are within the protection space specified by the Basic realm value of the current challenge. A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server. Similarly, when a client sends a request to a proxy, it may reuse a userid and password in the Proxy-Authorization header field without receiving another challenge from the proxy server. See section 4 for security considerations associated with Basic authentication.
    It also has this blurb when talking about the Digest method of authentication:
    Because the client is required to return the value of the opaque directive given to it by the server for the duration of a session, the opaque data may be used to transport authentication session state information. (Note that any such use can also be accomplished more easily and safely by including the state in the nonce.) For example, a server could be responsible for authenticating content that actually sits on another server. It would achieve this by having the first 401 response include a domain directi +ve whose value includes a URI on the second server, and an opaque directi +ve whose value contains the state information. The client will retry the +request, at which time the server might respond with a 301/302 redirection, pointing to the URI on the second server. The client will follow the redirection, and pass an Authorization header , including the <opaque> data.
    Which sounds like exactly what you are trying to do, so it may or may not be possible with the basic method. I suggest some experimentation (remember you can send named parameters to redirect() ) to see if you can get it to work. Please let us know what progress (or lack) you make.

      From what I recall, Digest authentication works just like Basic, with the exception that the CLIENT makes an MD5 hash of the password string (and perhaps other session data)before it sends the authentication data over the wire; w/ basic authentication, the password is sent in cleartext. Also, Netscape 4.x does not support Digest authentication. But again, I'd advise reading the RFCs.

      Philosophy can be made out of anything. Or less -- Jerry A. Fodor

        Right...but I'm talking about the possibility that information can be sent from server to client for use in the next client to server discussion...which the redirect header already does for URI. The question is
        1. How do we construct an Authorization header to pass back to the client (with the intention of the client using it)
          This is my attempt to find that info.

        2. Does it work, or does the client discard such data?
          Someone will have to test that.
Re: Passing a 401 header with CGI.pm ?
by swiftone (Curate) on Dec 02, 2000 at 01:33 UTC
    I'm a bit confused...accessing websites is done via the LWP module, not CGI. There have been many threads about this (try Hitting a web page).

    CGI can send a header, but CGI is sent to the client, not to a website.

    Update: Oh! a redirect, sorry, didn't read while brain was engaged. You don't want a 401 header (that's Requires Authorization), you want something more like a 302 (moved). Details won't matter CGI.pm can redirect with...redirect(). It's just a matter of finding what parameters to ask for. Lemmee root around the docs...

      I don't think it's just the redirect ... it seems that what he wants is something that will authorize the client in a non-standard manner. Personally, I'd think you need a pretty fancy solution for that (e.g. a mod_perl enabled server with a custom authentication handler; you'd set a cookie at some point, and the handler would read the cookie and if not present, restrict access).

      But there are many bright people here, I suppose they can come up with something less drastic.

      Philosophy can be made out of anything. Or less -- Jerry A. Fodor

        I think the issue is how the whole redirection trick works. If the browser does all the work, and the protocol allows you to pass the necessary authorization fields, it could happen. (Unfortunately, I don't know the answers to those questions).

        I've dug around in the CGI.pm code, and the redirect() function will pass additional arguments along, so that will work. It's just a matter of finding out IF such arguments exist, and if so, what they are.

Re: Passing a 401 header with CGI.pm ?
by mr_roboto (Initiate) on Dec 02, 2000 at 03:36 UTC
    Wow, I'm impressed with the overwhelming response to this in such a short time.

    Sorry about the confusion there with my Subject. Yeah basically what I'm trying to do is tell Apache the Username and Password through a form rather than through the browsers pop-up window. The RFC is some good reference material, and it's obvious I need to learn more about how the HTTP protocol works and how browsers and web servers talk to each other. Does anyone know any good reference materials on this subject, preferably online, off the top of their heads ?

    Thanks again for your prompt response !
      You can possibly POST your authentication information to the form, and then have the script redirect the user:
      http://username:password@www.example.com/resource/
      However, this puts the username and password not only in the clear for all to see, but in plain-text as part of the request.

      Or, if your username/password is fairly static (never changes through use of the form), just have the form post its results to a CGI script using a variation of the URL above.

      If you haven't already found it, good monk nop started this thread recently that contains some good links.
          cheers,
          Don
          striving for Perl Adept
          (it's pronounced "why-bick")