Skip to content
joshchan edited this page Aug 23, 2012 · 3 revisions

Basic CAS authentication works great for replacing traditional web based logins. But what about web services, such as when a web portal requests information directly from another server? There is no chance to redirect the user to the CAS server in such cases.

The CAS proxy feature allows one already authenticated service (e.g. web portal) to connect to another service (e.g. private RSS feed) on behalf of the user. From the second service's perspective, it will be almost exactly as if the user was directly making the request.

To accomplish this,have a server process that receives information from the CAS server. This can be an external server with a separate IP address, or on the same machine as the web server, or even the same process. This Node.js CAS client provides an internal callback server, and also supports running standalone as an external callback server, or even a full external proxy server.

In all cases, the callback server must be reachable on the same network (e.g. the public Internet) as the CAS server. That is, it will not work if you run the it behind a firewall. The CAS server needs to connect to it to give it information on users who are logging in.

There are several ways to do CAS proxying:

  1. CAS client + external CAS callback server
  2. CAS client + external CAS callback + proxy server
  3. CAS client + internal CAS callback server

Cases 1 and 2 can use this same Node.js CAS client running in standalone mode. In 1, the external CAS callback server acts only as a way to receive credentials from the CAS server. In 2, the difference is that it also forwards HTTP requests from the CAS client out to target services.

Standalone Callback + Proxy Server

The proxy server uses HTTPS. So you will need the usual SSL private key and certificate for this.

    // Initialize
    var fs = require('fs');
    var CAS = require('cas');
    var cas = new CAS({
        base_url: 'https://my-cas-server.example.com/cas',
        version: 2.0,
        proxy_server: true,
        
        // This port is for clients to connect to for forwarding HTTP requests
        proxy_server_port: 80443,
        
        // This host + port is for the CAS server to connect to.
        proxy_callback_host: 'my-public-domain.example.com',
        proxy_callback_port: 8989,
        
        // For SSL
        proxy_server_key: fs.readFileSync('/path/to/privat_key.pem'),
        proxy_server_cert: fs.readFileSync('/path/to/ssl_cert.pem')
    });

And that's it! The standalone proxy server is now running. If you don't want to forward requests on this external server, then just omit the proxy_server_port option.

Using an External Proxy Server

Let's say you have set up the standalone server on a separate machine. Now you want to actually use it to proxy requests.

    // Initialize
    var CAS = require('cas');
    var cas = new CAS({
        base_url: 'https://my-cas-server.example.com/cas',
        version: 2.0,
        
        // Note the port numbers and how they correspond to the previous example 
        external_pgt_url: 'https://my-public-domain.example.com:8989/',
        external_proxy_url: 'https://my-public-domain.example.com:80433/'
    });

Again, you can omit the external_proxy_url option if you do not want the external server to forward your HTTP requests. The local CAS client will make the requests directly instead, though still "proxied" in the sense that you are doing it under the identity of the logged in user.

Now you are ready to make proxied requests to other CAS-enabled services. But first we need to have the user log in the first time. This is the same as the basic CAS authentication, except now we keep track of the PGTIOU returned by the CAS server.

    var login = function(req, res) {
        cas.authenticate(req, res, function(err, status, username, extended) {
            if (err) throw err;
            
            console.log('User ' + username + ' logged in');
            
            var pgtIOU = extended['PGTIOU'];
            if (!pgtIOU) {
                // Proxy server down?
                // ...
            }
            else {
                // Now we have the PGTIOU. We can use it to make proxied requests.
                // In a real application you should store it in the session or something.
                
                cas.proxiedRequest(pgtIOU, {
                    protocol: 'http:',
                    method: 'GET',
                    hostname: 'my-other-server.example.com',
                    port: 80,
                    pathname: '/some/cas/service'
                }, function(err, proxyReq, proxyRes) {
                       // Handle the response from the other server
                       // ...
                });
            }
        });
    }

Using an Internal Proxy Server

We can combine the proxy server into our main web portal process too. Convenient for when there is no firewall separating the CAS client from the CAS server.

    // Initialize
    var CAS = require('cas');
    var cas = new CAS({
        base_url: 'https://my-cas-server.example.com/cas',
        version: 2.0,
        proxy_server: true,
        proxy_server_port: 0, // disable external proxy connections
        proxy_server_key: fs.readFileSync('/path/to/private_key.pem'),
        proxy_server_cert: fs.readFileSync('/path/to/ssl_cert.pem'),
        proxy_callback_host: 'my-public-domain.example.com',
        proxy_callback_port: 8989
    });

Then authenticate the user as usual, and keep track of the PGTIOU value.

    // Make a proxied request
    cas.proxiedRequest(pgtIOU, {
        protocol: 'http:',
        method: 'POST',
        hostname: 'my-other-server.example.com',
        port: '80',
        pathname: '/some/cas/service'
    }, function(err, proxyReq, proxyRes) {
        // Handle the response
        // ...
    });

Roll Your Own

Chances are, you already have your own preferred ways of making HTTP requests. If so, you don't need to use the provided proxiedRequest() method. Simply obtain a proxy ticket and include it in your request's query string.

    var service = "http://example.com/foo?msg=hello";

    // Obtain the user's proxy ticket
    cas.getProxyTicket(pgtIOU, function(err, pt) {
        // Append the ticket to your query string
        service += '&ticket=' + pt;

        // Request your service however you like
        $.ajax({
            url: service,
            ...
        });
    });