WebAuth V3 Technical Specification

Version 1.05 Modified 7/24/03

Contents

1. Introduction

1.1 WebAuth Components

The WebAuth protocol involves interactions between three basic components:
  1. User-Agent (UA)
  2. WebAuth-enabled Application Server (WAS)
  3. WebKDC
The desired goal of the WebAuth protocol is that a WAS will be able to securely identify a user accessing resources. It is also desired that single sign-on be achieved, so a user does not have to sign into each application separately, unless an application requires it for security reasons. The WAS can also choose to either trust the WebKDC to vouch for the identity of a user, or it can request the WebKDC use an authenticator, such as a Kerberos V5 service request (krb5_mk_req) that it can verify using its own Kerberos V5 identity.

1.1.1 User-Agent (UA)

The User-Agent is a web browser that supports cookies. No plugins, Java, or JavaScript are required. The UA will be redirected to the WebKDC as requested to by a WAS.

1.1.2 WebAuth-enabled Application Server (WAS)

A WebAuth-enabled Application Server is an Apache 2.0 server configured to run the WebAuth module. The WebAuth module will guard access to protected resources as configured by the administrator and/or application developer. If a user hasn't been identified yet (as determined by possessing a WAS cookie), they will be re-directed to the WebKDC, otherwise access will be granted.

All interactions between the UA and WAS SHOULD be SSL-protected, to prevent cookies being disclosed to an eavesdropper that could use those cookies to impersonate a user.

Although support will initially be provided only for Apache 2.0, it should be possible to support the protocol under other application servers, such as a Java application server (not running under Apache), and other servers like IIS. The protocol is also being designed so it doesn't require Kerberos V5 support, which should aid in making it flexible to port to other application servers, and support future standards as they arise.

1.1.3 WebKDC

The WebKDC is also an Apache 2.0 server, running a custom Apache module.

All interactions between the UA and WebKDC MUST be protected via SSL to prevent disclosure of passwords, as well as sensitive data such as the tokens which are used to provide single sign-on and access to application resources.

The WebKDC will listen for two different type of requests, which will most likely be distinguished by different URLs.

The first type of request are those requests coming from a UA after it has been redirected by a WAS. When the WebKDC receives one of these, it will first see if the request includes a cookie that contains cached credentials. If the request contains a valid cookie, this cookie is used to obtain a new credentials and the user is redirected back to the WAS.

If the cookie is not present or has expired, then the user will be prompted for their username and password. After submitting this information back to the WebKDC, the WebKDC will then verify the username and password, using the configured authenticated backend (Kerberos V5 by default). The WebKDC will the generate two "tokens". One is placed in a cookie scoped for the WebKDC and used to provide single sign-on in future requests, and one that gets sent back to the WAS, which will verify it upon receipt.

The second type of the WebKDC handles consist of XML messages that get POSTed to the WebKDC via HTTPS directly from a WAS. These message are used to request additional credentials for a user.

The WebKDC is also responsible for ensuring that the server making a request is authorized to make the request. For example, one WAS server can't use a "token" that was created for use by another WAS server.

1.2 WebAuth Tokens

Tokens provide a standard mechanism to exchange and store information that is cryptographically secure from both tampering and disclosure. Tokens also contain information such as creation times to help detect replays, and expiration times so data is never trusted/used forever. Tokens also enable authentication of the servers using them.

Tokens get transfered between servers using URL query parameters, POST data, cookies, and XML documents. They are AES-encrypted using either a private key, or a shared session key. They also include a SHA1 HMAC used to detect data modification/tampering.

There are currently nine different type of tokens, which are described in the following table.

Token Type Encrypted With Description
webkdc-service WebKDC's private AES-key The webkdc-service-token is used by WebAuth App Servers to communicate with the WebKDC. It contains a session key that is shared between the WebKDC and the WAS.

webkdc-service-tokens are created only by the WebKDC, and are used only by a WAS.

webkdc-proxy WebKDC's private AES-key The webkdc-proxy-token contains a user's proxied credentials (K5/K4 tgt). The WebKDC will only allow a webkdc-proxy-token to be used by the server it was originally generated for. The main use of a webkdc-proxy-token is by the WebKDC itself to implement single sign-on. Its secondary use is to allow WAS servers to request credential-tokens.
request session-key The request-token contains the request from a WAS for a token (usually an id-token) from the WebKDC. It is AES-encrypted in a session-key from a webkdc-service-token, and contains information like the return URL, type of token requested, etc. A webkdc-service-token is always included along with a request-token.
error session-key The error-token is returned in the event of an unrecoverable error that occurred while asking for a token with a request-token.
id session-key The id-token contains the identity of the user trying to access a resource. The WAS will verify the id-token, then construct an app-token for future use.
proxy session-key The proxy-token is used to return a webkdc-proxy-token to a WAS. It includes information about the webkdc-proxy-token, such as its expiration, its type, etc.
cred session-key The credential-token contains a user's credential (K5/K4 service ticket).
login WebKDC's private AES-key login-tokens are used by the WebKDC with the requestTokenRequest command. They contain the user's username and password and are used to obtain the inital webkdc-proxy-tokens.
app WAS private AES-key app-tokens are used by WAS servers to store data, such as the identity of a user after it has been verified from an id-token, a proxy-token, or credentials for future use.

Their exact formats are discussed in detail at the end of this document.

1.3 Security Model and Key Management

As mentioned in the previous section, tokens are at the heart of the WebAuth security model. They are used to authenticate requests and responses between servers, as well as protect data that is stored in URLs and cookie data. Shared symmetric keys (session-keys) are used to encrypt tokens between servers, and private keys are used to encrypt tokens meant for a only a single server to decrypt.

Also note that any tokens that appear in URLs are tokens that are only valid for a short-period of time (5 minutes by default).

Any attempt to re-use them after that will fail. If a user bookmarks a URL with a token in it (or hits their "back" button) then one of three things can happen:

  1. The user has a valid cookie. In this case, the token in the URL is ignored (but still stripped out from the URL before passing it to the app), so there is no problem.
  2. The user has no valid cookie, but the token in the URL is still valid. In this case, the user will get granted access, and a cookie will be created. Note that this can only happen until the token is no longer "fresh" (as mentioned, 5 minutes by default).
  3. The user has no valid cookie, and the token is too old. In this case, the user will be redirected to the login page is usual (the token will be stripped from the URL before the redirect).
1.3.1 Distributing and Managing Session Keys

In order to exchange session-keys, a key distribution protocol is needed. For this purpose, we use Kerberos V5 and SSL to bootstrap and get the session-keys. The WAS that needs a session-key will post an XML message to the WebKDC requesting a service-token. This message contains the result of krb5_mk_req call for the WebKDC's server principal, which the WebKDC uses to authenticate the server making the request. The WebKDC will then send back the service-token, and the session-key that is encrypted (in the WebKDC's key) inside of the service-token. The WAS will need both to make future requests to the WebKDC. The whole transaction itself is protected with SSL.

The thing to note here is that there are no long term keys stored on the WebKDC itself other its own private key. Once the WAS obtains the service-token and session-key, it will cache them both until the service-token expires, which will most likely be 24 hours. Before the service-token expires, the WAS must request a new service-token.

The protocol is also designed such that other authentication mechanisms may be used to bootstrap, such as the GSS-API, or SSL client authentication between servers.

One potential issue here is that we are using weaker DES keys (Kerberos V5 keytabs) to bootstrap into stronger AES keys. This issue is partially mitigated by the use of SSL to further protect the transaction. It was decided that this was acceptable in order to keep the WebKDC stateless and to re-use the existing Kerberos V5 infrastructure where possible.

1.3.2 Distributing and Managing Private Keys

Both the WebKDC and the WAS servers need private keys to encrypt their tokens with. These keys will be randomly-generated 128-bit AES keys (longer keys are supported if needed), that get stored in "key rings". The key ring is used to contain previously valid keys, currently valid keys, and keys that will be valid in the future. A command-line utility is provided to manage the key ring file.

The key ring consists of a number of entries, each of which contain the following information:

  key type       Type of the key, AES is the only supported key type right now.
  key data       The binary data that makes up the key
  creation       Time the key was created
  valid_after    When the key becomes valid

Of note is the "valid_after" value. It is used to create post-dated keys in the key ring, to allow for new keys to get generated and distributed among a pool of servers (for load balancing/fail-over) in such a way that all the keys can be updated before the key becomes valid.

Initially key rings can be distributed using a mechanism like SSH, though there will eventually be support within the WebAuth protocol for distributing new key rings among a pool of servers.

Stand-alone servers (not part of a pool) will automatically generate new keys when needed upon a restart.

1.3.3 Supporting Server Pools

In order to support server pools, we need to ensure that any server that receives a token is able to decrypt and verify it. Note that we are only talking about the WebAuth protocol here, applications must solve their own issues when dealing with a pool of servers.

For the WebKDC, all we need to do is distribute the key ring file across all the WebKDCs, and having a posted-dated new key in the key ring file makes this task easier.

For the WAS, there are two issues. First, we also need to distribute the key ring file so all WAS servers can decrypt their app-tokens. The second issue, is how to deal with session-keys.

Recall that responses from the WebKDC come back to the WAS encrypted in the session-key, not the WAS server's private key. If the response comes back to a different WAS server, it would not have the same session-key that another WAS server had. One way to deal with session-keys would be to share the same service-token and session-key across all the servers. This of course would be very painful and expensive to do.

Another way to handle this is make sure that the return-url in the request-token contains the real server's hostname/ip address as opposed to the virtual/pool address. This solves the problem as the request always comes back to WAS server that initiated the request. This approach however might not work in all environments. For example, the real app server's address may not be visible from outside the firewall.

The most flexible solution is to allow WAS servers to include some "state" or "context" in each request-token. The WebKDC would treat this state as opaque data, and return it to the WAS server along with the requested token. Note that it would be transmitted alongside the returned token, not inside of it. Now, the WAS server would create an app-token (which is encrypted in its private key), that contains the session-key, and use this as the state it sends in the request-token. The WebKDC return this state along with the response, and the WAS can decrypt the state using its private key to get the session-key.

2. WebAuth Scenarios

  1. Application Access, no tokens (Initial Sign-on)
  2. Application Access, app-token
  3. Application Access, no app-token, proxy-token (Single Sign-on)
  4. Application Access, no app-token, credentials required (Proxied Credentials)
  5. Logging Out

2.1 Application Access, no tokens (Initial Sign-on)

In the first scenario we discuss what happens when a user requests a webauth-protected resource and has not yet logged in. i.e., there is no app-token present, and there is no proxy-token present.

  ---------------------------------------------------------------
      UA                 WAS            WebKDC
  ---------------------------------------------------------------

      request resource
  1.  -----------------> WAS

      redirect to WebKDC
  2.  <----------------- WAS

      request-token for id-token
  3.  -------------------------------> WebKDC

      login form gets sent back
  4.  <------------------------------- WebKDC

      login form gets posted
  5.  -------------------------------> WebKDC

      confirmation page gets sent back
  6.  <------------------------------- WebKDC

      re-request resource (with returned token in URL)
  7.  -----------------> WAS

      set the app-token cookie, response from app 
  8.  <----------------- WAS

  ---------------------------------------------------------------

We will now go over each step in detail.
  1. UA requests a webauth-protected resource from WAS.

  2. mod_webauth detects the user doesn't have an app-token, and constructs a request-token for an id-token. The request-token contains information such as the return URL, requested type of token, etc. The request-token is encrypted using an AES session key shared between the WAS and the WebKDC obtained from a webkdc-service-token.

    mod_webauth then generates a redirect to the WebKDC (including the request-token in the URL query parameters).

  3. The redirect causes the user's browser (UA) to get sent to the WebKDC, along with the request-token. No cookies get sent to the WebKDC.

  4. The WebKDC will then decrypt the request-token. It will check the creation time to make sure the request is "fresh", and send back a login form to the UA. The request-token (or the information contained in it) will be placed in a hidden field.

  5. The user enters their username/password and submits the form back to the WebKDC.

  6. The WebKDC validates the username/password, and also makes sure the that the WAS is allowed to request identity tokens.

    Assuming the username/password is valid, the WebKDC will then construct a cookie for itself, which contains the proxy-token, and the id-token.

    A confirmation page will be sent back to the browser, that when ok'd will do a GET on the original request, with the id-token appended to the original request.

  7. The user's browser will now re-request the original resource, passing along the id-token in the URL.

  8. mod_webauth will see the id-token in the request, and will check to make sure that it is fresh. It will then look at the subject authenticator-type in the token to see if it needs to verify the subject. If the authenticator-type is krb5, then it needs to use its keytab to verify the identity of the subject, which is the result of a krb5-mk-req API call.

    After verifying the subject, mod_webauth will then rewrite the id-token into an app-token, and create an app-token to be placed into a cookie for future requests.

    mod_webauth will then strip off the token from the URL, and let the original app handle the request. Note that the tokens will not show up in the access_log.

2.2 Application Access, app-token

In the second scenario we discuss what happens when a user requests a webauth-protected resource and already has an app-token. This will be the most common case.

  ---------------------------------------------------------------
      UA                 WAS            WebKDC
  ---------------------------------------------------------------

      request resource
  1.  -----------------> WAS

      response
  2.  <----------------- WAS

  ---------------------------------------------------------------

We will now go over each step in detail.
  1. UA requests a webauth-protected resource from WAS.

  2. mod_webauth detects the user has a valid app-token and grants access.

2.3 Application Access, no app-token, proxy-token (Single Sign-on)

In the third scenario we discuss what happens when a user requests a webauth-protected resource and doesn't have an app-token, but has a proxy-token.

This is the "Single Sign-on" case, because the WebKDC can use the user's proxy-token to acquire access to the application without having to prompt the user for their password again.

  ---------------------------------------------------------------
      UA                 WAS            WebKDC
  ---------------------------------------------------------------

      request resource
  1.  -----------------> WAS

      redirect to WebKDC
  2.  <----------------- WAS

      request-token for id-token
  3.  -------------------------------> WebKDC

      redirect with returned token
  4.  <------------------------------- WebKDC

      re-request resource (with returned token in URL)
  5.  -----------------> WAS

      set the app-token cookie, response from app
  6.  <----------------- WAS

We will now go over each step in detail.
  1. UA requests a webauth-protected resource from WAS.

  2. mod_webauth detects the user doesn't have an app-token, and constructs a request-token for an id-token. The request-token contains information such as the return URL, requested type of token, etc. The request-token is encrypted using an AES session key shared between the WebKDC and the WAS obtained from a webkdc-service-token.

    mod_webauth then generates a redirect to the WebKDC (including the request-token in the URL query parameters).

  3. The redirect causes the user's browser (UA) to get sent to the WebKDC, along with the request-token.

  4. The WebKDC detects the user has a valid proxy-token, and uses it to construct a new id-token. The WebKDC will then construct the return URL, which will contain the response-token, and the requested-token (in the response-token).

  5. The user's browser will now re-request the original resource, passing along the id-token in the URL.

  6. mod_webauth will see the id-token in the request, and will check to make sure that it is fresh. It will then look at the subject authenticator-type in the token to see if it needs to verify the subject. If the authenticator-type is krb5, then it needs to use its keytab to verify the identity of the subject, which is the result of a krb5-mk-req API call.

    After verifying the subject, mod_webauth will then rewrite the id-token into an app-token, and create an app-token to be placed into a cookie for future requests.

    mod_webauth will then strip off the token from the URL, and let the original app handle the request.

  7. The UA will save the app-token cookie, and then re-request the original request.

  8. The application will now serve the original request.

2.4 Application Access, no app-token, credentials required (Proxied Credentials)

In the fourth scenario we discuss what happens when a user requests a webauth-protected resource and doesn't have an app-token, and the application needs to use credentials.

This is the "Proxy credentials" case. The WAS must request a proxy-token, which it will then use to request additional credentials via the XML interface. In this scenario we assume the WebKDC already has a proxy-token for itself.

  ---------------------------------------------------------------
      UA                 WAS                WebKDC
  ---------------------------------------------------------------

      request resource
  1.  -----------------> WAS

      redirect to WebKDC
  2.  <----------------- WAS

      request-token for proxy-token
  3.  ------------------------------------> WebKDC

      redirect with returned token
  4.  <------------------------------------ WebKDC


      re-request resource (with returned token in URL)
  5.  -----------------> WAS

                             request tokens 
  6.                     WAS -------------> WebKDC

                                 response
  7.                     WAS <------------- WebKDC

      set the cookies, response from app
  8.  <----------------- WAS

We will now go over each step in detail.
  1. UA requests a webauth-protected resource from WAS.

  2. mod_webauth (or mod_webauthproxy) detects the user doesn't have an app-token containing the required credentials, and constructs a request-token for a proxy-token. The request-token contains information such as the return URL, requested type of token, etc. The request-token is encrypted using an AES session key shared between the WebKDC and the WAS obtained from a webkdc-service-token.

    mod_webauth then generates a redirect to the WebKDC (including the request-token in the URL query parameters).

  3. The redirect causes the user's browser (UA) to get sent to the WebKDC, along with the request-token.

  4. The WebKDC detects the user has a valid proxy-token, and uses it to construct a new proxy-token. The WebKDC will then construct the return URL, which will contain the proxy-token.

  5. The user's browser will now re-request the original resource, passing along the proxy-token.

  6. mod_webauth will see the proxy-token in the request, and check to make sure the request is fresh. It will then use the webkdc-proxy-token to make an XML request to the WebKDC for an id-token, and for whatever credential-tokens it needs.

  7. The WebKDC will receive the webkdc-proxy-token, and make sure the webkdc-service-token's subject is allowed to be use the webkdc-proxy-token. If everything is ok, it will return the requested id and credential tokens.

  8. After verifying the id-token, mod_webauth will then rewrite the id-token into an app-token, and also re-encrypt the credential token(s) using its private key. All the tokens will be placed into cookies for future requests. The proxy-token can be discarded.

    mod_webauth will then strip off the token from the URL, and let the original app handle the request.

  9. The UA will save the cookies, and then re-request the original request.

  10. The application will now serve the original request.

    The credentials will be passed to the application by having mod_webauth save the credentials into a ticket file, and setting an environment variable to point to that ticket file. When the credential file gets created will most likely be based upon an Apache directive. Note: this needs to be looked at in more detail, and other options may be needed.

2.5 Logging Out

The easiest and most effective way to "logout" is to exit the browser and/or logout of the operating system itself. All the cookies that WebAuth uses are session cookies, meaning they are only valid for the duration of a browser's particular execution.

Aside from quitting the browser, there are two issues to consider when logging out:

  1. Logging out of a single application
  2. Logging out of all applications

2.4.1 Logging out of a single application

Logging out of an application involves having all the session cookies associated with a given application on a given server removed. This can be achieved by having a "logout" link available within the application itself. Clicking on the URL can cause a logout page to be displayed, that clears out all the cookies. It will be possible to configure mod_webauth to do this automatically for certain URLs (for example /mail/logout/), so the application doesn't have to do it itself.

mod_webauth can also be configured to send the user to a system-wide logout page on the WebKDC. This would create a more consistent logout experience for users. The logout page could also remind the user that they may be logged into other applications well, and could also have them click on a link to destroy their proxy-token as well.

It will also be possible for the application to specify a session timeout, such that app-tokens will become invalid if they aren't used within a certain period of time. This can also be combined with an option that causes the application to request that the proxy-token be ignored, effectively forcing the user to sign in again.

2.4.2 Logging out of all applications

As mentioned earlier, the easiest way to logout of all applications would be to exit the browser. Aside from that, two things would need to happen in order to "logout" from all applications:
  1. The user would need to go to the WebKDC's logout page so any proxy-tokens could get removed.
  2. All applications would need to be configured to timeout inactive sessions. That would force an application to make an id-token request, and there would be no proxy-token.

Note that its not possible for the WebKDC to remove individual app-token cookies, since those cookies are scoped to each application server.

3. WAS and WebKDC Conventions

3.1 Getting redirected to WebKDC

Each WAS will be configured with a URL to redirect a UA to in the event they need to make a request. When making the request, the WAS must include both the request-token and the webkdc-service-token (so the WebKDC can decrypt the request-token).

These tokens will be passed via query parameters in the URL. The format is:

  https://{webkdc-host}/{webkdc-login-uri}?RT=request-token;ST=service-token

Where RT is the base64-encoded request-token and ST is the base64-encoded service-token.

3.2 Getting redirected back to WebAuth App Server

When the WebKDC is ready to redirect the UA back to the WAS, it will construct a return URL by taking the return-url specified in the request token an appending "?WEBAUTHR=token;" to it, where token is the requested token. If the "as" attribute was specified in the request token, then it is base64-encoded and then appended to the URL (always after WEBAUTHR) as ";WEBAUTHS=token;".

For example:

  https://{return-url}?WEBAUTHR=token;[;WEBAUTHS=token;]

The WebAuth Apache module will then strip "?WEBAUTHR=token;" and ";WEBAUTHS=token;" off the end of the URL. If the tokens are valid, the WebAuth module will construct the app-token cookie, etc, then do another redirect back to the URL with the extra information stripped off.

4. WebKDC XML Protocol Description

This section describes the XML protocol used to talk to the WebKDC.

4.1 Protocol Overview

The protocol consists of an exchange of XML-formatted messages over a tcp-based connection. A protocol interaction consists of a single XML message from the client and a single XML message response from the server.

For any elements that require a base64-encoded value, there must be no whitespace after the open element and before the close element. For example, this is valid:

 <requestToken>{base64-webkdc-service-token}</requestToken>
while this is not:
 <requestToken>
            {base64-webkdc-service-token}
 </requestToken>

Having said that, this spec uses whitespace in examples for readability.

The message is posted to the WebKDC using HTTPS.

The command request format is:


  <xxxxRequest>
    <messageId>{message-id}</messageId>   <!-- optional, no default -->
    <protocolVersion>1</protocolVersion>  <!-- optional, default is 1 -->
    <!-- rest depends on command -->
  </xxxxRequest>
The command response format on success is:

  <xxxxResponse>
    <messageId>{message-id}</messageId>   <!-- only if present in request -->
    <!-- rest depends on command -->
  </xxxxResponse>

The command response format on failure is:

  <errorResponse>
    <messageId>{message-id}</messageId>   <!-- only if present in request -->
    <errorCode>{numeric}<errorCode>
    <errorMessage>{message}<errorMessage>
  </errorResponse>

4.2 Common Elements

The following elements are common cross all commands.

4.2.1 <messageId>

If a request message contains a <messageId> element, then the value used within this element MUST be returned in the response message corresponding to the request.

4.2.2 <protocolVersion>

This is an optional string that specifies the version of the protocol in use. The default value if not specified is 1. Currently, the only supported protocol is version 1, using any other value MUST return an error.

4.2.3 <errorCode>

This is a numeric error code that is present in an errorResponse message.

Error Code Description
1 The service-token used was expired.
2 The service-token used was corrupt and/or was unable to be decrypted.
3 The proxy-token used was expired.
4 The proxy-token used was corrupt and/or was unable to be decrypted.
5 The request was invalid. For example, a required element was missing, an attribute value was incorrect, the server was unable to parse the XML request, etc. The generally indicates a bug in the client.
6 The request was unauthorized. An attempt was made to request a token type that was not authorized.
7 The server had an internal error (out of memory, etc). If the client retries the request may succeed, though some external event might be causing the problem (Kerberos server being down, etc).
8 The request-token was stale.
9 The request-token was invalid.
10 Unable to obtain the requested credential.
11 The krb5 <requesterCredential> was bad.
12 The login-token was stale.
13 The login-token was invalid.
14 Login failed due to bad password or invalid username.
15 A webkdc-proxy-token of a certain type was required and was not present in the request.
16 The user hit the cancel button during the login process.
17 The WAS server requested a "forced" login.

4.2.4 <errorMessage>

This is a textual description of the error code, meant to be human readable, but not actually displayed to the user (it is not localized).

4.3 Commands

There are currently two commands defined, getTokens and requestToken. The getTokens command is used directly by the WAS to request a service token, cred-tokens, etc. The requestToken command is used by the web front-end on the WebKDC itself to process a request-token sent in a URL from a WAS.

4.3.1 getTokens

The getTokens command is used to request tokens (service, id, proxy). The subject's credentials are a proxy-token obtained on behalf of the user.

The request message is:


  <getTokensRequest>

    <requesterCredential type="service|krb5">
       <!-- for type="service" -->
       {base64-webkdc-service-token}
       <!-- for type="krb5" -->
       {base64-krb5-mk-req-data}
    </requesterCredential>

    <subjectCredential type="proxy">
       <proxyToken>{base64-webkdc-proxy-token}</proxyToken>
       <!-- additional <proxyToken>...</proxyToken> here -->
    </subjectCredential>

    <!-- present when requestCredential is of type="service" -->
    <requestToken>{base64-request-token}</requestToken>

    <tokens>
      <token type="id|proxy|service|cred" id="{id-to-use-in-response}">
        <!-- for type="id" -->
         <authenticator type="krb5|webkdc"/> <!-- optional, default is krb5 -->
        <!-- for type="proxy" -->
         <proxyType>krb5|krb4</proxyType>
        <!-- for type="cred" -->
         <credentialType>krb5|krb4</credentialType>
         <serverPrincipal>{krb4/krb5-service}</serverPrincipal>
      </token>
      <!-- additional <token>...</token> requests go here -->
    </tokens>
  </getTokensRequest>
The response message is:

  <getTokensResponse>
    <tokens>
      <token id="{id-from-request}">
        <tokenData>{base64}</tokenData>
        <!-- for type="service" -->
        <sessionKey>{base64-session-key}</sessionKey>
        <expires>{expiration-time}</expires>
      </token>
      <!-- additional <token>...</token> responses go here -->
    </tokens>
  </getTokensResponse>

If any errors occur, then an <errorResponse> message will be returned.

requesterCredentials of type "krb5" can only be used to obtain a service-token, they can't be used for obtaining any other tokens, nor can a credential of type "service" be used to request another service-token.

subjectCredentials are required when requesting id, proxy, or cred tokens.

4.3.2 requestToken

The requestToken command is used by the web front-end running on the WebKDC to process a request-token sent in a URL from a WAS.

The request message is:


  <requestTokenRequest>

   <requesterCredential type="service">
       {base64-webkdc-service-token}
   </requesterCredential>

   <subjectCredential type="proxy|login">
     <!-- for type="proxy" -->
     <!-- need to pass in all the existing proxy-tokens, since
          we (the web front-end) don't know which we might need -->
     <proxyToken>...</proxyToken>
     <!-- additional <proxyToken>...</proxyToken> here -->
     <!-- for type="login" -->
     <loginToken>...</loginToken>     
   </subjectCredential>

   <!-- request token from WAS -->
   <requestToken>{base64-request-token}</requestToken> 

   <!-- request info from front-end, used with S/Ident support -->
   <requestInfo>
     <localIpAddr>n.n.n.n</localIpAddr>
     <localIpPort>nnnn</localIpPort>
     <remoteIpAddr>n.n.n.n</remoteIpAddr>
     <remoteIpPort>nnnn</remoteIport>
   </requestInfo>

  </requestTokenRequest>
The response message is:

  <requestTokenResponse>

    <!-- loginErrorCode will be set in a requestTokenResponse if 
         there was an error related to logging in  -->
    <loginErrorCode>{numeric}<loginErrorCode>
    <loginErrorMessage>{message}<loginErrorMessage>

    <!-- any updated/new proxy tokens created, only passed back
         if subjectCredential was type="login"  -->
    <proxyTokens>
      <proxyToken type="...">{base64-proxy-token}</proxyToken>
    </proxyTokens>

    <!-- the url to return to the user to -->
    <returnUrl>...</returnUrl>

    <!-- subject inside of service-token used to make request -->
    <requesterSubject>...</requesterSubject>

    <!-- subject from subjectCredential -->
    <subject>...</subject>

    <!-- requestedToken will either be an error, id, or proxy token. 
         not set if <loginErrorCode> is set. -->
    <requestedToken>{base64-token}</requestedToken>

    <!-- set if request token request options has "lc" -->
    <loginCanceledToken>{base64-error-token}</loginCanceledToken>

    <!-- app state is the opaque app state passed in the
         request token that we hand back to WAS -->
    <appState>{base64-state}</appState>

  </requestTokenResponse>

If any non login-related errors occur, then an <errorResponse> message will be returned.

If a login-related error occurs then a <requestTokenResponse> will be returned, but <requestedToken> will be unset, and <loginErrorCode> will be set. The error codes used by <loginErrorCode> are a subset of those used by <errorCode>:

Error Code Description
14 Login failed due to bad password or invalid username. The web front-end should re-prompt for username/password.
15 A webkdc-proxy-token of a certain type was required and was not present in the request. The web front-end should prompt for the username and password.
17 The WAS server requested a "forced" login. The web front-end should prompt for the username and password.

If the request option attribute in the request token has "lc" in it, then <loginCanceledToken> will be returned. Note that it is up to the WebKDC web front-end to send back that token as the requested token (WEBAUTHR in the return URL) if the user hits the cancel button.

If the request option attribute in the request token has "fa" in it and we are not passing a login token, then this command will return a <loginErrorCode> of 17, which indicates that the user is being forced to (re)login. Any passed in proxy-tokens are ignored.

4.3.3 webkdcProxyToken

The webkdcProxyToken command is used to convert an existing credential (like a Kerberos TGT) into a webkdc-proxy-token.

The request message is:


  <webkdcProxyTokenRequest>

   <subjectCredential type="krb5">
       <!-- for type="krb5" -->
       {base64-krb5-mk-req-data}
   </subjectCredential>

   <proxyData>
       <!-- for subjectCredential type="krb5" -->
       {base64-krb5-mk-priv-on-tgt}
   </proxyData>

  </webkdcProxyTokenRequest>
The response message is:

  <webkdcProxyTokenResponse>

    <webkdcProxyToken>{base64-proxy-token}</webkdcProxyToken>
  
    <!-- subject from subjectCredential -->
    <subject>...</subject>

  </webkdcProxyTokenResponse>

If any errors occur an <errorResponse> message will be returned.

4.3.4 webkdcProxyTokenInfo

The webkdcProxyTokenInfo command is used to get information about an existing webkdc-proxy-token.

The request message is:


  <webkdcProxyTokenInfoRequest>

    <webkdcProxyToken>{base64-proxy-token}</webkdcProxyToken>

  </webkdcProxyTokenRequest>
The response message is:

  <webkdcProxyTokenInfoResponse>
    <subject>...</subject>
    <proxyType>...</proxyType>
    <creationTime>...</creationTime>
    <expirationTime>...</expirationTime>
  </webkdcProxyTokenInfoResponse>

If any errors occur an <errorResponse> message will be returned.

4.4 Posting XML to WebKDC

The WebKDC will be an apache 2.0 module. The XML data should be POSTed to the following URL:
https://{webkdc-server}/webkdc-service/

The Content-Type should be "text/xml".

Initially only SSL will be supported. If performance becomes a concern, a non-ssl port may be opened up, in which case the content will need to be encrypted, most likely using AES.

4.5 Examples

4.5.1 WAS asking for a webkdc-service-token

In this example, the WAS requests a webkdc-service-token for itself. This happens when the WAS needs to acquire a webkdc-service-token to talk to the WebKDC. It will cache the token until it nears expiration time, at which point it will request a new one.

<getTokensRequest>
   <requesterCredential type="krb5">
      {base64-krb5-mk-req-data}
   </requesterCredential>
   <tokens>
     <token type="service" id="0"/>
   </tokens>
</getTokensRequest>

<getTokensResponse>
  <tokens>
    <token id="0">
      <sessionKey>{base64-session-key}</sessionKey>
      <expires>{expiration-time}</expires>
      <tokenData>{base64}</tokenData>
    </token>
  </tokens>
</getTokensResponse>

4.5.2 WAS asking for a credential-token

In this example, the WAS requests an K5 ticket using its webkdc-proxy-token. The webkdc-proxy-token's type must match the requested credential's type.

 <getTokensRequest>
   <requesterCredential type="service">
       {base64-webkdc-service-token}
   </requesterCredential>

   <requestToken>{base64-request-token}</requestToken>

   <subjectCredential type="proxy">
     {webkdc-proxy-token}
   </subjectCredential>
  <tokens>
    <token type="credential" id="0"/>
      <credentialType>krb5</credentialType>
      <serverPrincipal>service/ldap@stanford.edu</serverPrincipal>
    </token>
  </tokens>
</getTokensRequest>

<getTokensResponse>
  <tokens>
    <token id="0">
      <tokenData>{base64}</tokenData>
    </token>
  </tokens>
</getTokensResponse>

The WebKDC will verify that the webkdc-proxy-token was granted to the same server specified inside of the webkdc-service-token.

5. Tokens

5.1 Token Encoding

All encrypted-tokens have the following general encoding:

{key-hint}{nonce}{hmac}{token-attributes}{padding}

Everything except {key-hint} is AES-encrypted.

key-hint is UNIX UTC time stored in network byte order, and is four bytes long. It does not get encrypted, and is only used as a *hint* for the server to determine which key to use to decrypt the token. It should not be used for any other purpose as its value is not protected from modification.

nonce is 16 random bytes, and gets encrypted with the rest of the data in the token. It is used to ensure that two tokens with the same data and same encryption key don't encrypt to the same value.

hmac is the sha1 hmac of the actual data (name/value pairs), including the padding. The key used with HMAC is the AES private-key. Note, a better solution would be to use a different key, but that requires have two keys or using a key-derivation function to derive the HMAC key from the AES key. One approach would be to use a key-derivation function like TLS uses.

token-attributes consists of a sequence of name=value pairs, separated by a ';' character. names are not allowed to contain an "=" or a ";", and values MAY contain binary data, but MUST escape any ";"'s in the data with an extra ";".

For example, if we had the following names and values:

  a=1
  msg=hello;there
  bin={binarydata}
  b=2

They would get encoded as:

  a=1;msg=hello;;there;bin={binarydata};b=2;

padding is any additional padding of the data to make the length a multiple of 16 bytes for AES encryption. Note, there is always padding present. If the length is already a multiple of 16, then 16 bytes of padding will be added. The value of each padding byte will be equal to the length of the padding. For example, if the padding length is 7, then each byte will be equal to 0x07.

Tokens get base64 encoded before being used as a cookie, query parameter, or within XML data.

5.2 Assigned Token Attributes

The following is the list of assigned token attributes. All attributes used within tokens are listed in this table.

All time values are stored in network byte order, and are the number of seconds since 00:00:00 UTC, January 1, 1970.
Name Encoding Description
asbinary The app-token that is optionally included within a request-token that will get returned along with the response to the request-token.
cmdstring name of XML command being executed (i.e., getTokensRequest)
crd binary credential data (i.e., an encoded krb5 service ticket)
crsstringcredential server princpal
crtstringcredential type (krb4, krb5)
ctbinary timecreation time of the token. For tokens used to exchange messages between servers (request, error, id, proxy, credential) , this value is used to ensure that the request is "fresh". For example, tokens of this type with a ct older then 5 minutes will get flagged by the server (WAS or WebKDC) as being "stale".
ec stringerror code from WebKDC. FIXME: need to document the valid error codes, they will be the same as the error codes returned in XML messages.
emstring error message from WebKDC. Should only be used for logging and/or debugging, since it not localized or in a format meant for end-user consumption.
etbinary timeexpiration time of the token
kbinaryAES session key.
ltbinary timelast-used time. If this attribute is set in an app-token, then it will be periodically updated as the token is used.
pstringuser's password
pdbinaryproxy data (i.e., an encoded krb5 tgt)
ps stringproxy subject, the subject from the service-token used when the proxy-token was granted.
ptstringproxy type (krb4/krb5)
rostring comma-separated list of request options: lc = return the app an error code if login is canceled, fa = forced authentication (app is requesting "live" authentication)
rttstringrequested token type: id = id-token, proxy = proxy-token
rustringreturn URL. The url to return the user to after authenticating with the WebKDC
sstringauthenticated subject. In service-tokens this is the server that authenticated to get the service-token, in all other tokens it is the user that authenticated. Server subjects have the form "type:identifier". Currently, the only defined type is "krb5", and the identifier is the krb5 principal (i.e., krb5:host/lichen.stanford.edu@stanford.edu).
sastringsubject authenticator type (krb5, webkdc)
sadbinarysubject authenticator data. For sa of type krb5 this is a krb5_mk_req using the same krb5 principal as the service-token's subject
tstringtoken type: webkdc-service, webkdc-proxy, req, error, id, proxy, cred, app. Used by a server to ensure that a token is being used for the correct purpose.
wt binarywebkdc-{proxy,service}-token (the webkdc tokens inside another token)
ustringuser's username

5.3 Specific Token Encodings

The following sections describe which attributes are include with which tokens.

5.3.1 webkdc-service-token encoding

webkdc-service-tokens are used by WAS servers to communicate with the WebKDC. They get returned from WebKDC after an entity authenticates with the WebKDC using the XML-interface while requesting a service-token.


  t=webkdc-service
  k={session-key}
  s=krb5:{requesting-servers-k5-principal}
  ct={creation-time}
  et={expiration-time}

All attributes are AES-encrypted in WebKDC's private key. The server that initially requested the token would also have received {session-key} and {expiration-time} out-of-band from the token itself and stored them for the duration of the token.

5.3.2 webkdc-proxy-token encoding

A webkdc-proxy-token is a k4/k5 TGT maintained by the WebKDC on behalf of another user, or on behalf of itself.

  t=webkdc-proxy
  ps={subject-from-webkdc-service-token-used-to-get-proxy-ticket}
  pt=krb5|krb4|...
  s={username}
  pd={proxy-data}
  ct={creation-date}
  et={expiration-date}

All attributes are AES-encrypted in the WebKDC's private key.

When a webkdc-proxy-token is used, the WebKDC checks that the subject in the service-token making the request is authorized to used the webkdc-proxy-token granted to the ps subject.

5.3.3 request-token encoding

A request-token gets sent to the WebKDC server by WAS, along with the WAS's webkdc-service-token. They are used to request tokens via the HTML interface, and are also used with the XML interface (in a restricted form) to allow the WebKDC to verify that a request being made with a service-token is both recent, and for the specified command.

The first form is used with the requestTokenRequest command:


  t=req
  ct={creation-time}
  [as={binary-app-token-used-for-state}]
  ru={return-redirect-url}
  [ro=fa,lc]
  rtt=id|proxy
  # for rt=id
  sa=krb5|webkdc
  # for rt=proxy
  pt=krb5|krb4

The second form is used with the getTokensRequest command:


  t=req
  ct={creation-time}
  cmd={xml-command-we-are-going-to-execute}

All attributes are AES-encrypted in the webkdc-service-token session-key.

The value of ct is used to prevent replay attacks. Values older then a certain time (probably 5 minutes by default) should be rejected as a replay.

cmd indicates which XML command we are invoking (i.e., getTokensRequest). The WebKDC will compare this command against the name of the command in the XML.

5.3.4 error-token encoding

An error-token gets sent from the WebKDC as a response to a request-token when an error has occurred.

  t=error
  ct={creation-time}
  ec={error-code}]
  em={error-message}]

All attributes are AES-encrypted in the webkdc-service-token session-key

The value of ct is used to prevent replay attacks. Values older then a certain time (probably 5 minutes by default) should be rejected as a replay.

5.3.5 id-token encoding

The id-token gets returned from WebKDC, and is bound to an WAS. They are used to express the identity of user.

  t=id
  sa=krb5|webkdc
  # for sa=webkdc
  s={username}
  # for sa=krb5
  sad={result-of-krb5-mk-req-for-webauth/hostname}
  ct={creation-time}
  et={expiration-time}

All attributes are AES-encrypted in the webkdc-service-token session-key

If "at" is krb5, then "s" (subject) is the result of a call to krb5_mk_req for the webauth/WAS service using the user's tgt.

If "at" is webkdc, then we are trusting the webkdc and "s" already contains the users's principal name.

The value of "ct" is used to prevent replay attacks. Values older then a certain time (probably 5 minutes by default) should be rejected as a replay, as id-tokens are only used once and re-written into an app-token.

The value of "et" is used to let the application know how long the subject of the id-token should be considered valid for. The value for "et" is the expiration time of the proxy-token used to create it.

5.3.6 proxy-token encoding

An proxy-token gets returned when a WAS requests a proxy-token from the WebKDC.

  t=proxy
  pt=krb5|krb4|...
  s={username}
  wt={webkdc-proxy-token}
  ct={creation-time}
  et={expiration-time}

All attributes are AES-encrypted in the webkdc-service-token session-key

The value of "ct" is used to prevent replay attacks. Values older then a certain time (probably 5 minutes by default) should be rejected as a replay, as proxy-tokens are only used once and re-written into an app-token.

wt is the webkdc-proxy-token (in binary form) that was returned as the result of the request.

5.3.7 credential-token encoding

A credential-token is k4/k5 service ticket returned by the WebKDC for a WAS.

  t=cred
  crs={server-principal}
  crt=krb5|krb4|...
  s={username}
  cd={credential-data}
  ct={creation-date}
  et={expiration-date}

All attributes are AES-encrypted in the in a webkdc-service-token session-key

5.3.8 login-token encoding

A login-token is created by the web front-end running on the WebKDC when it needs to authenticate a user.

  t=login
  ct={creation-time}
  p={password}
  u={username}

All attributes are AES-encrypted in the WebKDC's private key.

5.3.9 app-token encoding

An app-token is controlled/maintained by a WAS. The main use of an app-token is to cache the idenity within an id-token after it has been verified.

  t=app
  et={expiration-time}
  [ct={creation-time}]
  [s={username}]
  [k=session-key]
  [lt={last-use-time}]

All attributes are AES-encrypted in the WAS's private key.

app-tokens are created by mod_webauth on first receipt of an id-token. After the id-token is verified, it gets converted into an app-token.

last-use-time is only included if the webauth module is configured to keep track of it.

session-key is only present when an app-token is being used as the application-state inside of a request-token.

6. Cookie conventions

Cookies are used to hold tokens for future use by a UA. All cookies are scoped to a single server, there are no domain-wide cookies. All cookies will be encoded in base64 before passing them to the UA.

The tokens that get used in cookies are webkdc-proxy-tokens, proxy-tokens, app-tokens, and cred-tokens. The following naming convention will be used to name cookies.

NOTE: proxy-tokens and cred-tokens get decrypted using a session key, but will get re-encrypted using the WAS private key before getting stored in a cookie.

app-token cookies will have the name:

  webauth_at

webkdc-proxy-token cookies have the following naming convention:

  webauth_wpt_{proxy-type}

Where proxy-type is krb4 or krb5 depending on the type of the proxy-token.

proxy-tokens will have following naming convention:

  webauth_pt_{proxy-type}

Where proxy-type is krb4 or krb5 depending on the type of the proxy-token.

cred-tokens will have the following naming convention:

  webauth_ct_{cred-type}_{service-name}

where cred-type is krb4 or krb5 depending on the type of the cred-token. service-name will be the krb4 or krb5 service name, potentially with special characters escaped. XXX: need to compare valid characters in a krb4 and krb5 service name against valid characters in a cookie name.


7. Apache Directives

FIXME: Insert link to mod_webauth documentation

Revision History:

Date Version Author Changes
10/02/2002 0.1 schemers
  • first draft
10/04/2002 0.2 schemers
  • fixed typos, added more text in section 1.2
  • Use SHA1 everywhere instead of MD5
  • Change attribute separator in tokens from "\n" to ";"
  • Change time attributes inside of tokens from 4 byte binary network byte order to ASCII strings.
  • Changed description of time prepended to tokens from creation-time to key-hint
10/07/2002 0.3 schemers
  • Major changes, combine LS and WKDC into WebKDC
10/26/2002 0.4 schemers
  • Change time attributes inside of tokens back to 4 byte binary network byte order.
  • get rid of default values for attributes
  • requested token is now contained within the response-token
  • got rid of unneeded/not-fleshed-out token attributes (rth, san, ver)
  • add new token attributes (rt, rt-t, rt-et, sad)
  • change id-token to use "sad" instead of "s" for krb5
10/30/2002 0.5 schemers
  • use errorResponse message for indicating errors
  • remove app names for now, will re-specify when requirements are more clear
  • change "et" in id-token to "se", to indicate when the subject expires.
11/01/2002 0.6 schemers
  • rename service-token and proxy-token to webkdc-service-token and webkdc-proxy-token, to signify they are only used by the webkdc
  • remove response-token, add error-token and new proxy-token. The response from a request-token is either an error-token, id-token, or proxy-token
  • change "se" back to "et". Expiration times in tokens indicate when the token expires and when any data within the token (subject info, proxy data, etc) expires
  • simplify XML messages by placing base64-encoded data directly in <requestCredential> and <subjectCredential> instead of having them in another tag
  • wrap tokens in <tokens> element to make it easier to iterate through all the tokens in a <getTokensRequest>
11/03/2002 0.61 schemers
  • add new form of request-token that gets sent in XML requests along with a service-token to indicate which XML command is being requested.
  • update <requesterCredential> for type "service" to include <serviceToken> and <requestToken>.
11/05/2002 0.7 schemers
  • rename prt/prd/pro attributes to pt/pd/ps
  • formatting and clarification
11/07/2002 0.8 schemers
  • add section on security model and support for sending application-state in a request token to enable server pools.
11/08/2002 0.85 schemers
  • Forgot to final webauth redirect in the usage scenarios that sets the cookies and re-requests original URL.
11/13/2002 0.90 schemers
  • undo 0.85, since after initial Apache prototyping I think we can get away without doing the extra redirect.
  • add some text to "Security Model" section describing what happens if a user bookmarks or replays a url with a token in it.
11/14/2002 0.91 schemers
  • don't rewrite proxy/cred-tokens into app-tokens before storing them in cookies, just re-encrypt them using the WAS private key. This is so they can't be used as app-tokens.
  • change name of proxy/webkdc-proxy cookies
12/04/2002 0.99 schemers
  • document error codes for <errorCode>
  • add new requestToken command for the WebKDC web front-end to use to talk to the WebKDC.
  • url for webkdc post is now /webkdc-service/
  • remove line that says an <errorCode> can occur within a <token> returned in a <getTokensResponse>. The whole request succeeds or fails.
  • update format for <SubjectCredential> to be consistent across XML commands.
  • update format for <requesterCredential> by moving <requestToken> out into enclosing element, which also removes the need for the <serviceToken> element. Also consistent across commands.
  • only subject names in service-tokens have the form "krb5:{principal}", subject names referring to users will have only the username (unless krb5 is used and the call to krb5_aname_to_localname fails, in which case the fully-qualified principal name will be used).
  • remove inactivity timeout value from token.
  • change rr (request-reason) token attribute to ro (request-options)
  • add "lc" (login-canceled) and "fa" (forced-authentication) request options.
  • add <loginCanceledToken> to requestToken command response.
  • add two new error codes for forced-authentication and login-canceled.
12/06/2002 1.00 schemers
  • Add <loginErrorCode> and <loginErrorMessage> to <requestTokenResponse>.
12/12/2002 1.01 schemers
  • keys in the keyring no longer expire. The key with the most current (but not post-dated) valid_after will always be used to encrypt new keys.
1/14/2003 1.02 schemers
  • return <subject> in <requestToken> response so web front-end can display the authenticated username.
1/20/2003 1.03 schemers
  • fix typo in credential token encoding example, change "ct" to "crt".
  • add "crs" attribute to credential token
1/29/2003 1.04 schemers
  • When appending WEBAUTHR to return URL, use "?WEBAUTHR=...;" instead of ";WEBAUTHR=...;" so browsers handle relative URLs correctly.
7/24/2003 1.05 schemers
  • Added webkdcProxyToken command
  • Added webkdcProxyTokenInfo command
  • Added requestInfo to requestToken command for S/Ident support