The login page is a JavaServer Pages (JSP) and before attempting to edit the page you should be familiar with at least to basics of JSPs. Go out to the web and read a few tutorials (e.g. one, two, three).
To make changes to the login page and deploy them:
While nearly everything on the login page can be customized a few pieces must always be there:
<%=request.getAttribute("actionUrl")%>
j_username
input field must be used for the username.j_password
input field must be used for the user's password.These elements can be moved around in the page, but the input fields must be located within the form.
From IdP version 2.3.0 a taglib is available to make certain commonly used fields easily available to the jsp pages. Many of these tags are aimed at making it simple to extract the information described in <mdui:UIInfo>
extensions, as described here.
Declare this library by putting this statement
<%@ taglib uri="urn:mace:shibboleth:2.0:idp:ui" prefix="idpui" %>
at the top of the jsp file. This makes the tags described below available.
Depending on the precise metadata, some tags may not be able to find appropriate results. In such a situation the body of the tag (if present) is used.
Tag Format: <idpui:serviceName/>
Typical Outputs: "sp.example.org
" or "The Example SP
" or "urn:foo:bar:1:24
"
This provides a user-friendly name for the service provider. No HTML markup is applied. Specifically, for services identified by metadata, it will:
<mdui:DisplayName>
of the appropriate language in a <mdui:UIInfo>
extension attached to the SP's <md:SPSSODescriptor>
.<md:ServiceName>
of the appropriate language in an <md:AttributeConsumingService>
for the SP.EntityID
is a URL, then the hostname part of the URL is used.EntityID
is used.The resulting string is then inserted into the page with no markup.
In the case that the SP is unidentified by metadata (typically meaning support for "anonymous" relying parties has been enabled) the tag will output a default string. In V2.4.0 and later, the default can be overridden with a tag attribute called defaultValue
.
Tag Format: <idpui:serviceDescription>Default Value</idpui:serviceDescription>
Typical Outputs: "An example SP, used for something exciting
" or "Default Value
"
This provides a user-friendly description the service provider. No HTML markup is applied. Specifically it will:
<mdui:Description>
of the appropriate language in a <mdui:UIInfo/>
extension attached to the SP's <md:SPSSODescriptor>
.<md:ServiceDescription>
of the appropriate language in an <md:AttributeConsumingService>
for the SP.The resulting string is then inserted into the page with no markup.
Tag Format: <idpui:organizationName>Default Value</idpui:
organizationName
>
Typical Outputs: "Widgets R Us
" or "Default Value
"
This outputs a language-aware version of the <md:OrganizationName>
element from the SP's <md:SPSSODescriptor>
or surrounding <md:EntityDescriptor>
, or the tag body in the event no such value exists.
The resulting string is then inserted into the page with no markup.
Note that many metadata sources may not populate this information in a manner useful for display in such a context. This will vary by federation.
Tag Format: <idpui:organizationDisplayName>Default Value</idpui:
organizationDisplayName
>
Typical Outputs: "Widgets R Us
" or "Default Value
"
This outputs a language-aware version of the <md:OrganizationDisplayName>
element from the SP's <md:SPSSODescriptor>
or surrounding <md:EntityDescriptor>
, or the tag body in the event no such value exists.
The resulting string is then inserted into the page with no markup.
Note that many metadata sources may not populate this information in a manner useful for display in such a context. This will vary by federation.
Tag Format: <idpui:organizationURL linkText="text" cssClass="class" cssId="id" cssStyle="style">default text</idpui:
organizationURL
>
Typical Outputs: "<a href="http://widgets.com" id="id">text</a>
" or "default text
"
This searches the <md:Organization>
element attached to the SP's <md:SPSSODescriptor>
for an <md:OrganizationURL/>
in the browser's language. If no such entry is found then the body of the tag is inserted. Otherwise the found URL is placed inside an <a href="URL" class="cssClass" id="cssId" style="cssStyle"/>text</a>
element where
cssClass
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have a class
attribute.cssId
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an id
attribute.cssStyle
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an style
attribute.Note that many metadata sources may not populate this information in a manner useful for display in such a context. This will vary by federation.
Tag Format: <idpui:serviceContact name="text" cssClass="class" cssId="id" cssStyle="style" contactType="type">default</idpui:serviceContact>
Typical Output: "<a href="mailto:contact@sp.example.edu" class="ContactClass">Technical Support</a>
"
This provides the appropriate contact information for the service provider. Specifically, the contactType
attribute (default value "support" is used to locate a <md:ContactPerson>
entry for the SP.
The display text is taken from the contents of the name attribute. If the attribute is not present then the name is extracted appropriately from the sub elements of the <md:ContactPerson>
.
If no name is found then the body of the tag will be inserted.
If the <md:EmailAddress>
is not present in the <md:ContactPerson>
then the name is inserted with no additional markup.
Otherwise the name is rendered a hyperlink with the contents of the <EmailAddress>
being the reference, the name being the body and the "cssClass", "cssId", "cssStyle" attributes from the tag being used (if present) to set the "class", "id", "style" attributes respectively in the <a>
element.
For example given the following metadata segment
<ContactPerson contactType="support"> <GivenName>John</GivenName> <SurName>Doe</SurName> </ContactPerson> <ContactPerson contactType="technical"> <GivenName>Jane</GivenName> <SurName>Smith</SurName> <EmailAddress>mailto:tech@example.edu</EmailAddress> </ContactPerson> <ContactPerson contactType="billing"> <EmailAddress>mailto:billing@example.edu</EmailAddress> </ContactPerson> |
Then
<idpui:serviceContact cssId="cp"/>
would return John Doe
.<idpui:serviceContact cssStyle="font-size:20px" contactType="technical"/>
would return <a href="mailto:tech@example.edu" style="font-size:20px">Jane Smith</a>
<idpui:serviceContact cssClass="cpClass" name="Technical contact" contactType="technical"/>
would return <a href="mailto:tech@example.edu" class="cpClass">Technical contact</a>
<idpui:serviceContact name="contact" contactType="billing"/>
would return<a href="mailto:billing@example.edu">contact</a>
<idpui:serviceContact contactType="
administrative">Your SP's administrators</idpui:serviceContact>
would return Your SP's administrators
Tag Format: <idpui:servicePrivacyURL linkText="text" cssClass="class" cssId="id" cssStyle="style">default text</idpui:servicePrivacyURL>
Typical Outputs: "<a href="http://sp.example.edu/PrivacyStatement.html" id="privacyStatement">Privacy Statement</a>
" or "default text
"
This searches the <mdui:UIInfo>
extension attached to the SP's <md:SPSSODescriptor>
for a <mdui:PrivacyStatementURL/>
for the browser's language. If no such entry is found then the body of the tag is inserted. Otherwise the found URL is placed inside an <a href="URL" class="cssClass" id="cssId" style="cssStyle"/>text</a>
element where
cssClass
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have a class
attribute.cssId
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an id
attribute.cssStyle
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an style
attribute.Tag Format: <idpui:serviceInformationURL linkText="text" cssClass="class" cssId="id" cssStyle="style">default</idpui:serviceInformationURL>
Typical Outputs: "<a href="http://sp.example.edu/Information.html" class="info">Further Information</a>
" or "default
"
This searches the <mdui:UIInfo>
extension attached to the SP's <md:SPSSODescriptor>
for a <mdui:InformationURL/>
for the browser's language. If no such entry is found then the body of the tag is inserted. Otherwise the found URL is placed inside an <a href="URL" class="cssClass" id="cssId" style="cssStyle"/>text</a>
element where
cssClass
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have a class
attribute.cssId
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an id
attribute.cssStyle
attribute of the tag. If there is no such attribute then the resultant hyperlink will not have an style
attribute.Tag Format: <idpui:serviceLogo cssId="Id" cssClass="class" cssStyle="style" minWidth="integer" maxWidth="integer" minHeight="integer" maxHeight="integer">default</idpui:serviceLogo>
Typical Output: <img src="https://sp.example.edu/Information.html" class="logo" />
Name | Default Value | Controls |
---|---|---|
alt | same as | The text to associate with the alt attribute |
minHeight | 0 | The minimum height of any returned logo |
maxHeight | MaxInt | The maximum height of any returned logo |
minWidth | 0 | The minimum width of any returned logo |
maxWidth | MaxInt | The maximum width of any returned logo |
cssId | <none> | If present, the value is included as the id for the image ( |
cssClass | <none> | If present, the value is included as the class for the image ( |
cssStyle | <none> | If present, the value is included as the style for the image ( |
This tag searches the content of the <mdui:UIInfo>
element attached to the SP's <md:SPSSODescriptor>
for a <mdui:Logo>
element in the browser's language (or one with no language associated), suitably constrained by the attributes of the <idpui:serviceLogo>
element. If such an <mdui:Logo>
element is found, then result is
<img src="URL" [cssId="suppliedIdIfPresent"] [class="suppliedCssClassIfPresent
"
] alt="suppliedText" />
Otherwise the result is the content of the <idpui:serviceLogo>
element (if present).
Federations that rely on the Shibboleth IdP are encouraged to make recommendations to service providers about their metadata extensions.
As of IdP release 2.1.3 information regarding the current login process is more easily available. This information can be gotten through the use of the HttpServletHelper.
Almost all of the |
The most useful object available via this Helper is the LoginContext and its SAML protocol specific subclasses ShibbolethSSOLoginContext (if it's a SAML 1 request) or a SAML2LoginContext (if it's a SAML 2 request).
The login context will give you access to information such as the entity ID of the relying party (the service provider), the requested authentication methods, the authentication method the IdP is attempting to perform, whether the authentication is a forced and/or passive authentication, etc.
So, to get the LoginContext
you would do the following:
<%@ page import="edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper" %> <%@ page import="org.opensaml.util.storage.StorageService" %> <%@ page import="edu.internet2.middleware.shibboleth.idp.authn.LoginContext" %> <% StorageService storageService = HttpServletHelper.getStorageService(application); LoginContext loginContext = HttpServletHelper.getLoginContext(storageService,application, request); %> |
You could then use the LoginContext
like this:
Shibboleth Identity Provider Login to Service Provider <%= loginContext.getRelyingPartyId() %> |
Another useful set of information is the EntityDescriptor metadata for the relying party. This can include information such as human readable names and descriptions, informational URLs, etc. To get to the metadata for the relying party you would do the following:
<%@ page import="edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper" %> <%@ page import="org.opensaml.util.storage.StorageService" %> <%@ page import="edu.internet2.middleware.shibboleth.idp.authn.LoginContext" %> <%@ page import="edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager" %> <%@ page import="org.opensaml.saml2.metadata.EntityDescriptor" %> <% StorageService storageService = HttpServletHelper.getStorageService(application); LoginContext loginContext = HttpServletHelper.getLoginContext(storageService,application, request); RelyingPartyConfigurationManager rpConfigMngr = HttpServletHelper.getRelyingPartyConfigurationManager(application); EntityDescriptor metadata = HttpServletHelper.getRelyingPartyMetadata(loginContext.getRelyingPartyId(), rpConfigMngr); %> |
One significant drawback to the use of JAAS as the authentication provider within the IdP is that it does not bubble up very useful authentication errors. It simply indicates whether the authentication failed or succeeded. You can determine if an authentication attempt failed by checking that the request attribute LoginHandler.AUTHENTICATION_EXCEPTION_KEY
is not null.
For example:
<%@ page import="edu.internet2.middleware.shibboleth.idp.authn.LoginHandler" %> <% if (request.getAttribute(LoginHandler.AUTHENTICATION_EXCEPTION_KEY) != null) { %> <p><font color="red">Authentication Failed</font></p> <% } %> |
If you are using Microsoft ActiveDirectory as your authentication realm, you can make use of the sub error codes that MSAD sends along with the LDAP failure code of 49.. details here.
Another common error comes from the misuse of the IdP. The login page can not be accessed directly, it can only be accessed after the IdP has done some initial processing of a valid authentication request. However, some users will mistakenly access the login page because they bookmarked it, found it in their browser's history, or by means of the back button. The best way to detect this is to look for the presence of the LoginContext
and, if not available, display an appropriate error message.
For example:
<%@ page import="edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper" %> <%@ page import="org.opensaml.util.storage.StorageService" %> <%@ page import="edu.internet2.middleware.shibboleth.idp.authn.LoginContext" %> <% StorageService storageService = HttpServletHelper.getStorageService(application); LoginContext loginContext = HttpServletHelper.getLoginContext(storageService,application, request); %> <% if (loginContext == null) {%> <p><font color="red">Error:</font> Direct access to this page is not supported. Please ensure that you did not bookmark this page or reach it by using the back button or selecting it from your browser history. To log in to a particular service, please visit that service first.</p> <% } else { %> <!-- normal login page display goes here --> <% } %> |
Display login pages within a frame is a very common means of performing a "click-jacking" attack. The aforementioned OWASP page provides a "frame buster" javascript that can be used to attempt to prevent this kind of attack. It also identifies the X-FRAME-OPTIONS
header. This header can be set as follows:
<% response.addHeader("X-FRAME-OPTIONS", "DENY"); %> |