A simple CAS server that uses Auth0 as the backing IDP.
Presently Auth0 natively supports three authentication protocols for your applications: OpenID Connect, SAML, and WS-Federation (we support many more for your backing IDPs). However, there are many applications that use CAS (Central Authentication Service) to perform authentication and SSO.
This sample demonstrates a simple service written in Node.js that acts as a protocol translator between CAS and Auth0 (using OpenID Connect). This allows an application that only knows how to interact with a CAS server to leverage all of the capabilities of Auth0 as an IDP (SSO, federation with other IDPs like social and enterprise, security, etc).
This CAS server implementation takes advantage of the fact that both OpenID Connect and CAS are redirect-based protocols using the browser. Therefore, applications don't know or care that the CAS server has redirected the user to a different website (Auth0) to perform the actual authentication. All that matters is that the CAS protocol itself is honored between the application and the CAS Server.
Another implementation detail is that the CAS Server is completely stateless. To manage browser session it uses an encrypted cookie. It also reuses the Auth0 code
value as the CAS ticket
so there's no need to temporarily store a user's profile. The CAS ticket
is intended to be single-use, which works seamlessly with the Auth0 code
, which is also single-use.
- An application (aka CAS service) determines that the user needs to authenticate (e.g., it detects there is no local session), so it redirects the browser to the CAS Login endpoint, passing the
service
parameter, which identifies the application:
Application redirect ->
https://AUTH0_CAS_SERVER/login?service=SERVICE
- The CAS Server verifies that the
SERVICE
identifier points to a registered CAS service. If so, it performs an OpenID Connect authorization code flow with Auth0, redirecting to the/authorize
endpoint:
CAS Server redirect ->
https://AUTH0_DOMAIN/authorize?client_id=SERVICE_CLIENT_ID&response_type=code&scope=openid%20profile&redirect_uri=https://AUTH0_CAS_SERVER/callback&connection=AUTH0_CONNECTION&state=1234abcd
- The user logs into the IDP of whatever connection was configured (
AUTH0_CONNECTION
) and Auth0 redirects back to the CAS Server callback, providing an OAuth2code
andstate
:
Auth0 redirect ->
https://AUTH0_CAS_SERVER/callback?code=4242xyz&state=1234abcd
- The CAS Server then redirects back to the application passing the
code
received by Auth0 as the CASticket
:
Auth0 redirect ->
https://example.com/app?ticket=4242xyz
- Now the application can perform a server-to-server call to the CAS Validate endpoint to exchange the CAS
ticket
for the authenticated user profile:
Application:
GET https://AUTH0_CAS_SERVER/p3/serviceValidate?service=SERVICE&ticket=4242xyz
- Under the hood, the CAS Server calls the Auth0
/oauth/token
endpoint to complete the OpenID Connection authorization code flow:
CAS Server:
POST https://AUTH0_DOMAIN/oauth/token
{
"code": "CAS TICKET",
"client_id": "AUTH0_CLIENT_ID",
"client_secret": "AUTH0_CLIENT_SECRET",
"grant_type": "authorization_code",
"redirect_uri": "https://AUTH0_CAS_SERVER/callback"
}
- Auth0 responds with an
id_token
which contains the claims that are used to generate the expected user profile CAS response:
<?xml version="1.0"?>
<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas">
<cas:authenticationSuccess>
<cas:user>user1@example.com</cas:user>
<cas:attributes>
<cas:email>user1@example.com</cas:email>
<cas:email_verified>false</cas:email_verified>
...
</cas:attributes>
</cas:authenticationSuccess>
</cas:serviceResponse>
NOTE: The CAS Server can also return JSON if the
Accept: application/json
header is passed.
Create a Machine-to-Machine Application in Auth0 (eg. with the name CAS Server
) that the server can use to read app data. Configure the app so it's authorized to call the Auth0 Management API with the following scopes:
read:clients
read:client_keys
Capture the Client ID and Client Secret of this app as it will be used in the next step (API_V2_CLIENT_ID
and API_V2_CLIENT_SECRET
).
Create one or more applications in Auth0 that will represent your CAS Services. To signify an application as a CAS Service, add the following Application Metadata item:
cas_service
: The identifier of the CAS Service which is also the URL that the server will redirect to once authentication is complete (eg.https://example.com/cas
).
AUTH0_DOMAIN=your-tenant.auth0.com
API_V2_CLIENT_ID=m2m-app-client_id
API_V2_CLIENT_SECRET=m2m-app-client_secret
AUTH0_CONNECTION=connection-name
AUTH0_SCOPES="openid profile"
SECURE_COOKIE=false
SESSION_SECRET=a-hard-to-guess-secret
CAS_USERNAME_FIELD=auth0-user-profile-field-like-email
npm start
Make sure the CAS Service app in your Auth0 tenant has the following URL configured in its Allowed Callback URLs field:
http://localhost:3000/callback
http://localhost:3000/login?service=SERVICE
where:
SERVICE
is one of the CAS Service identifiers you configured in the CAS Service Applications section.
When the browser flow is complete you will be redirected back to your service's URL with a ticket query param:
SERVICE?ticket=TICKET
where:
SERVICE
is the CAS Service identifier used to begin the flowTICKET
is the CAS ticket generated for the authentication transaction
You can then call the validate endpoint to obtain the authenticated user profile:
curl "http://localhost:3000/p3/serviceValidate?service=SERVICE&ticket=TICKET"
npm test
Check them out here.
If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.
This project is licensed under the MIT license. See the LICENSE file for more info.