I. How to use OAuth server support configured for CAS server ?
Once you will have configured your CAS server with OAuth server support, you will be able to communicate with it through OAuth 2.0 protocol.
1) /oauth2.0/authorize
It's the url to call to authorize the user : the CAS login page will be displayed and the user will authenticate. After successful authentication, the user will be redirected on OAuth callbackUrl with a code. Input GET parameters required : client_id and redirect_uri.
2) /oauth2.0/accessToken
It's the url to call to exchange the code for an access token. Input GET parameters required : client_id, redirect_uri, client_secret and code.
3) /oauth2.0/profile
It's the url to call to get the profile of the authorized user. Input GET parameter required : access_token. The response is in JSON format with all attributes of the user.
II. How to add OAuth server support in CAS server ?
1) Add dependency
First step is to add the dependency to the OAuth cas support module in the CAS server webapp pom.xml :
<dependency> <groupId>org.jasig.cas</groupId> <artifactId>cas-server-support-oauth</artifactId> <version>${project.version}</version> <scope>runtime</scope> </dependency>
2) Add the OAuth20WrapperController
To add the OAuth20WrapperController, you need to add the mapping between the /oauth2.0/* url and the CAS servlet in the web.xml :
<servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/oauth2.0/*</url-pattern> </servlet-mapping>
You have to create the controller itself in cas-servlet.xml :
<bean id="oauth20WrapperController" class="org.jasig.cas.support.oauth.web.OAuth20WrapperController" p:loginUrl="http://mycasserverwithoauthwrapper/login" p:servicesManager-ref="servicesManager" p:ticketRegistry-ref="ticketRegistry" p:timeout="7200" />
The loginUrl is the login url of the CAS server. The timeout is the lifetime of a CAS granting ticket (in seconds, not in millisecondes !)
with its mapping in the handlerMappingC bean (cas-servlet.xml) :
<bean id="handlerMappingC" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/serviceValidate">serviceValidateController</prop> ... <prop key="/statistics">statisticsController</prop> <prop key="/oauth2.0/*">oauth20WrapperController</prop> </props> </property> <property name="alwaysUseFullPath" value="true" /> </bean>
3) Add the needed CAS services
3.1) Callback Authorization
One service is need to make the OAuth wrapper works in CAS. It defines the callback url after CAS authentication to return to the OAuth wrapper as a CAS service.
Note: the callback url must in fact end with "callbackAuthorize".
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"> <property name="registeredServices"> <list> <bean class="org.jasig.cas.services.RegisteredServiceImpl"> <property name="id" value="0" /> <property name="name" value="HTTP" /> <property name="description" value="oauth wrapper callback url" /> <property name="serviceId" value="${server.prefix}/oauth2.0/callbackAuthorize" /> </bean> ...
Starting with CAS 4, this configuration is made more explicit such that specific OAuth Services are now recognized by CAS:
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"> <property name="registeredServices"> <list> <!-- A dedicated component to recognize OAuth Callback Authorization requests --> <bean class="org.jasig.cas.support.oauth.services.OAuthCallbackAuthorizeService"> <property name="id" value="0" /> <property name="name" value="HTTP" /> <property name="description" value="oauth wrapper callback url" /> <!-- By default, only support regex patterns if/when needed --> <property name="serviceId" value="${server.prefix}/oauth2.0/callbackAuthorize" /> </bean> ...
3.2) OAuth Clients
A second service is necessary to register an OAuth client:
- The name and the description of the CAS service are the key and secret of the OAuth client.
- The theme is the actual service name that is to be used in the UI when services are asked for Authorization once the CAS login session has been established.
For each OAuth client, a CAS service needs to be added in configuration.
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"> <property name="registeredServices"> <list> <bean class="org.jasig.cas.services.RegisteredServiceImpl"> <property name="id" value="1" /> <property name="name" value="the_key_for_caswrapper1" /> <property name="description" value="the_secret_for_caswrapper1" /> <property name="serviceId" value="oauth client service url" /> <property name="theme" value="TheActualServiceName" /> </bean> ...
Starting with CAS 4, this configuration is made more explicit such that specific OAuth Services are now recognized by CAS:
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"> <property name="registeredServices"> <list> <bean class="org.jasig.cas.support.oauth.services.OAuthRegisteredService"> <property name="id" value="1" /> <property name="name" value="serviceName" /> <property name="description" value="Service Description" /> <!-- Supports regex patterns by default for service ids --> <property name="serviceId" value="oauth client service url" /> <property name="clientId" value="client id goes here" /> <property name="clientSecret" value="client secret goes here" /> </bean> ...
Note that there are specific properties, clientId and clientSecret dedicated to OAuth clients for configuration.
3.3) (Optional) CAS OAuth Client using another CAS OAuth Server
If you have one CAS server configured with the CasWrapperProvider20 (the client) to communicate with a CAS server wrapping OAuth 2.0 protocol (the server), you have the name and description of the service in CAS « server » matching the key and secret of the identity provider defined in the CAS « client » :
<bean class="org.jasig.cas.services.RegisteredServiceImpl"> <property name="id" value="1" /> <property name="name" value="the_key_for_caswrapper1" /> <property name="description" value="the_secret_for_caswrapper1" /> <property name="serviceId" value="http://mycasserver/login" /> </bean> <bean id="caswrapper1" class="org.jasig.cas.support.oauth.provider.impl.CasWrapperProvider20"> <property name="key" value="the_key_for_caswrapper1" /> <property name="secret" value="the_secret_for_caswrapper1" /> <property name="callbackUrl" value="http://mycasserver/login" /> <property name="serverUrl" value="http://mycasserverwithoauthwrapper/oauth2.0" /> </bean>
III. Technical presentation of the OAuth server mode
1) General
To reply to OAuth calls, the CAS server has a specific controller (OAuth20WrapperController), listening on a specific url (for example : /oauth2.0/*).
This wrapper delegates the authentication to the standard CAS authentication process (the callback is done by using a specific CAS service which is the callback url). It means that after being authenticated by the OAuth wrapped CAS server, the user is also authenticated in CAS.
The OAuth codes generated on the « authorize » OAuth calls are in fact CAS service tickets and the OAuth access tokens generated on the « access token » OAuth calls are in fact granting tickets.
OAuth client configurations are defined with CAS services : the key and secret of the OAuth clients have to be defined by the name and description of a CAS service to make OAuth client be « authorized » to use OAuth CAS server.
2) Classes & interfaces
a) OAuthConstants (org.jasig.cas.support.oauth)
This class has the constants for the OAuth implementation.
b) OAuthUtils (org.jasig.cas.support.oauth)
This class has some usefull methods to output data in plain text, handle redirects or add parameter in url.
c) BaseOAuthWrapperController (org.jasig.cas.support.oauth.web)
This controller is the base controller for wrapping OAuth protocol in CAS. It finds the right sub controller to call according to the url.
d) OAuth20WrapperController (org.jasig.cas.support.oauth.web)
This controller is the main entry point for OAuth version 2.0 wrapping in CAS, should be mapped to something like /oauth2.0/*. Dispatch request to specific controllers : authorize, accessToken...
e) OAuth20AuthorizeController (org.jasig.cas.support.oauth.web)
This controller is in charge of responding to the authorize call in OAuth protocol. It stores the callback url and redirects user to the login page with the callback service.
f) OAuth20CallbackAuthorizeController (org.jasig.cas.support.oauth.web)
This controller is called after successful authentication and redirects user to the callback url of the OAuth application. A code is added which is the service ticket retrieved from previous authentication.
g) OAuth20AccessTokenController (org.jasig.cas.support.oauth.web)
This controller returns an access token which is the CAS granting ticket according to the service and code (service ticket) given.
h) OAuth20ProfileController (org.jasig.cas.support.oauth.web)
This controller returns a profile for the authenticated user (identifier + attributes), found with the access token (CAS granting ticket).
i) OAuthRegisteredService (org.jasig.cas.support.oauth.services)
CAS 3, OAuth clients are registered via the above component in the service registry. Allows for options to define service name, client id and secret.
j) OAuthCalllbackAuthorizeService (org.jasig.cas.support.oauth.services)
CAS 4, the callback authorize service is to be defined via the above specific service in the service registry.