Shibboleth authentication adapter for the Zend Framework

I have been running some sites under a Shibboleth Service Provider for quite a long time. In some cases I had to modify applications written in PHP to be able to use Shibboleth authentication. But now I reached the point, when I had to implement Shibboleth authentication in my own application. Since I’m using Zend Framework for years, the natural way to achieve that was implementing a Shibboleth authentication adapter for the Zend_Auth component.

Actually, the task is quite simple, because all the required information is contained in the environment variables. The only “challenge” is to make sure the adapter is as generic and flexible as possible, so it can be deployed in various environments and applications. The configuration options for the adapter mostly specify the names of the attributes containing relevant information and possibly a mapping between the attribute names and the local names. Typical usage:

1
2
3
4
5
6
7
8
9
10
11
12
$auth = Zend_Auth::getInstance();
 
$authAdapter = new ShibbolethAdapter(array(
        'identityVar' => 'id', 
        'attrMap' => array(
            'uid' => 'id', 
            'cn' => 'name',
            'mail' => 'email'
        )
));
 
$result = $auth->authenticate($authAdapter);




If the authentication is successfull, the result variable will contain an array of attributes with re-mapped indexes, for example:

1
2
3
4
5
array(
    'id' => 'ivan',
    'name' => 'Ivan Novakov',
    'email' => 'ivan.novakov[at]debug.cz'
)


Of course, if your application requires the user identity in another format or even as an object (which is quite usual :) ), it is possible to change that behaviour by subclassing the adapter and implementing your own _createResult() method. The same method may also implement additional authentication checks. For example, many applications, which use Shibboleth authentication, have a local user database and the user identity received from Shibboleth has to be validated against it.

See the complete soource code for more details. Please, note, that this is a simple piece of code, the main purpose of which is to inspire. Feel free to use it and modify it to suit your needs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
 * Authentication adapter for the Zend Framework authentication component Zend_Auth.
 * 
 * @author Ivan Novakov <ivan.novakov@debug.cz>
 */
class ShibbolethAdapter implements Zend_Auth_Adapter_Interface
{
 
    /**
     * The configuration object.
     * 
     * @var Zend_Config
     */
    protected $_config = NULL;
 
    /**
     * Default values for all the options.
     * 
     * @var array
     */
    protected $_defaultOptions = array(
 
        // an attribute prefix to apply to all the attributes
        'attrPrefix' => '', 
        // the attribute value separator, if the attribute has multiple values
        'attrValueSeparator' => ';', 
 
        // the attribute holding the session string
        'sessionIdVar' => 'Shib-Session-ID', 
        // the attribute containing the entityID of the IdP
        'idpVar' => 'Shib-Identity-Provider', 
        // the attribute containing the application ID (referenced in shibboleth2.xml)
        'appIdVar' => 'Shib-Application-ID', 
        // the attribute conatining  the time of the authentication
        'authInstantVar' => 'Shib-Authentication-Instant', 
        // the attribute containing the authentication context
        'authContextVar' => 'Shib-AuthnContext-Decl', 
 
        // the attribute used to determine the identity of the user
        'identityVar' => 'uid', 
 
        // an array, which maps Shibboleth attributes to local user attributes
        'attrMap' => array(
            'eppn' => 'uid', 
            'cn' => 'cn', 
            'mail' => 'email'
        )
    );
 
    /**
     * Array of environment variables.
     * 
     * @var array
     */
    protected $_env = array();
 
 
    /**
     * Constructor.
     * 
     * @param array $config
     * @param array $env
     */
    public function __construct (Array $config = array(), Array $env = NULL)
    {
        $this->_config = new Zend_Config($config + $this->_defaultOptions);
 
        if (! $env) {
            $env = $_SERVER;
        }
        $this->_env = $env;
    }
 
 
    /**
     * Implementation of the authenticate() method.
     * 
     * @see Zend_Auth_Adapter_Interface::authenticate()
     */
    public function authenticate ()
    {
        if (! $this->_isSession()) {
            return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, NULL, array(
                'no_session'
            ));
        }
 
        $userAttrs = $this->_extractAttributes();
        if (! isset($userAttrs[$this->_config->identityVar])) {
            return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, NULL, array(
                'no_identity'
            ));
        }
 
        if (is_array($userAttrs[$this->_config->identityVar])) {
            return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS, NULL, array(
                'multiple_id_attr_value'
            ));
        }
 
        return $this->_createResult($userAttrs);
    }
 
 
    /**
     * Convenience method for creating a positive result with user identity.
     * 
     * Can be overrided in subclasses in case the user identity is required in another format (an object for instance).
     * 
     * @param array $userAttrs
     */
    protected function _createResult (Array $userAttrs)
    {
        return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $userAttrs);
    }
 
 
    /**
     * Returns the array of available attributes.
     * 
     * @return array
     */
    protected function _extractAttributes ()
    {
        $attrs = array();
        foreach ($this->_config->attrMap->toArray() as $srcIndex => $dstIndex) {
            if ($value = $this->_getEnv($srcIndex)) {
                $values = explode($this->_config->attrValueSeparator, $value);
                if (count($values) > 1) {
                    $attrs[$dstIndex] = $values;
                } else {
                    $attrs[$dstIndex] = $value;
                }
            }
        }
 
        return $attrs;
    }
 
 
    /**
     * Returns true, if there is an existing Shibboleth session.
     * 
     * @return bool
     */
    protected function _isSession ()
    {
        return ($this->_getSession());
    }
 
 
    /**
     * Returns the current Shibboleth session string.
     * 
     * @return string
     */
    protected function _getSession ()
    {
        return $this->_getEnv($this->_config->sessionIdVar);
    }
 
 
    /**
     * Returns the required environment variable.
     * 
     * @param string $index
     * @return string
     */
    protected function _getEnv ($index)
    {
        $index = $this->_config->attrPrefix . $index;
 
        if (isset($this->_env[$index])) {
            return $this->_env[$index];
        }
 
        return NULL;
    }
}

2 Comments

  1. James Johnson:

    I’m research shibboleth for use with Zend Framework. What license is your code under? Thanks!

  2. Ivan Novakov:

    It’s just an example code, so you can use any part of it for anything you want :) .

Leave a comment

ERROR: si-captcha.php plugin says GD image support not detected in PHP!

Contact your web host and ask them why GD image support is not enabled for PHP.

ERROR: si-captcha.php plugin says imagepng function not detected in PHP!

Contact your web host and ask them why imagepng function is not enabled for PHP.