2014-05-27 14:29:04 +00:00
|
|
|
<section id="javascript-adapter">
|
2014-02-25 00:58:54 +00:00
|
|
|
<title>Pure Client Javascript Adapter</title>
|
|
|
|
<para>
|
|
|
|
The Keycloak Server comes with a Javascript library you can use to secure pure HTML/Javascript applications. It
|
2014-04-30 13:38:02 +00:00
|
|
|
works in the same way as other application adapters except that your browser is driving the OAuth redirect protocol
|
2014-02-25 00:58:54 +00:00
|
|
|
rather than the server.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
The
|
|
|
|
disadvantage of using this approach is that you end up having a non-confidential, public client. This can be mitigated
|
|
|
|
by registering valid redirect URLs. You are still vulnerable if somebody hijacks the IP/DNS name of your pure
|
|
|
|
HTML/Javascript application though.
|
2014-04-30 13:38:02 +00:00
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
To use this adapter, you must first configure an application (or client) through the <literal>Keycloak Admin Console</literal>.
|
|
|
|
You should select <literal>public</literal> for the <literal>Client Type</literal> field. As public clients can't
|
|
|
|
be verified with a client secret you are required to configure one or more valid redirect uris as well.
|
|
|
|
Once you've configured the application click on the <literal>Installation</literal> tab and download the <literal>keycloak.json</literal>
|
|
|
|
file. This file should be hosted in your web-server at the same root as your HTML pages. Alternatively you can either
|
|
|
|
specify the URL for this file, or manually configure the adapter.
|
|
|
|
</para>
|
2014-02-25 00:58:54 +00:00
|
|
|
<para>
|
2014-04-30 13:38:02 +00:00
|
|
|
Next you have to initialize the adapter in your application. An example on how to do this is shown below.
|
2014-02-25 00:58:54 +00:00
|
|
|
<programlisting><![CDATA[
|
|
|
|
<head>
|
2014-04-30 13:38:02 +00:00
|
|
|
<script src="http://<keycloak server>/auth/js/keycloak.js"></script>
|
2014-02-25 00:58:54 +00:00
|
|
|
<script>
|
2014-04-30 13:38:02 +00:00
|
|
|
var keycloak = Keycloak();
|
|
|
|
keycloak.init().success(function(authenticated) {
|
|
|
|
alert(authenticated ? 'authenticated' : 'not authenticated');
|
|
|
|
}).error(function() {
|
|
|
|
alert('failed to initialize');
|
2014-02-25 00:58:54 +00:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
</head>
|
2014-04-30 13:38:02 +00:00
|
|
|
]]></programlisting>
|
|
|
|
To specify the location of the keycloak.json file:
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
var keycloak = Keycloak('http://localhost:8080/myapp/keycloak.json'));
|
|
|
|
]]></programlisting>
|
|
|
|
Or finally to manually configure the adapter:
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
var keycloak = Keycloak({
|
|
|
|
url: 'http://keycloak-server/auth',
|
|
|
|
realm: 'myrealm',
|
|
|
|
clientId: 'myapp'
|
|
|
|
});
|
|
|
|
]]></programlisting>
|
|
|
|
You can also pass <literal>login-required</literal> or <literal>check-sso</literal> to the init function. Login
|
|
|
|
required will redirect to the login form on the server, while check-sso will redirect to the auth server to check
|
|
|
|
if the user is already logged in to the realm. For example:
|
|
|
|
<programlisting><![CDATA[
|
2014-05-14 16:46:19 +00:00
|
|
|
keycloak.init({ onLoad: 'login-required' })
|
2014-02-25 00:58:54 +00:00
|
|
|
]]></programlisting>
|
|
|
|
</para>
|
2014-04-30 13:38:02 +00:00
|
|
|
|
2014-02-25 00:58:54 +00:00
|
|
|
<para>
|
|
|
|
After you login, your application will be able to make REST calls using bearer token authentication. Here's
|
|
|
|
an example pulled from the <literal>customer-portal-js</literal> example that comes with the distribution.
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
<script>
|
|
|
|
var loadData = function () {
|
|
|
|
document.getElementById('username').innerText = keycloak.username;
|
|
|
|
|
|
|
|
var url = 'http://localhost:8080/database/customers';
|
|
|
|
|
|
|
|
var req = new XMLHttpRequest();
|
|
|
|
req.open('GET', url, true);
|
|
|
|
req.setRequestHeader('Accept', 'application/json');
|
|
|
|
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
|
|
|
|
|
|
|
req.onreadystatechange = function () {
|
|
|
|
if (req.readyState == 4) {
|
|
|
|
if (req.status == 200) {
|
|
|
|
var users = JSON.parse(req.responseText);
|
|
|
|
var html = '';
|
|
|
|
for (var i = 0; i < users.length; i++) {
|
|
|
|
html += '<p>' + users[i] + '</p>';
|
|
|
|
}
|
|
|
|
document.getElementById('customers').innerHTML = html;
|
|
|
|
console.log('finished loading data');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
req.send();
|
|
|
|
};
|
|
|
|
|
|
|
|
var loadFailure = function () {
|
|
|
|
document.getElementById('customers').innerHTML = '<b>Failed to load data. Check console log</b>';
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
var reloadData = function () {
|
2014-04-30 13:38:02 +00:00
|
|
|
keycloak.updateToken().success(loadData).error(loadFailure);
|
2014-02-25 00:58:54 +00:00
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
2014-04-30 13:38:02 +00:00
|
|
|
<button onclick="loadData()">Submit</button>
|
2014-02-25 00:58:54 +00:00
|
|
|
]]></programlisting>
|
2014-04-30 13:38:02 +00:00
|
|
|
</para>
|
2014-02-25 00:58:54 +00:00
|
|
|
<para>
|
|
|
|
The <literal>loadData()</literal> method builds an HTTP request setting the <literal>Authorization</literal>
|
|
|
|
header to a bearer token. The <literal>keycloak.token</literal> points to the access token the browser obtained
|
|
|
|
when it logged you in. The <literal>loadFailure()</literal> method is invoked on a failure. The <literal>reloadData()</literal>
|
|
|
|
function calls <literal>keycloak.onValidAccessToken()</literal> passing in the <literal>loadData()</literal> and
|
|
|
|
<literal>loadFailure()</literal> callbacks. The <literal>keycloak.onValidAcessToken()</literal> method checks to
|
|
|
|
see if the access token hasn't expired. If it hasn't, and your oauth login returned a refresh token, this method
|
|
|
|
will refresh the access token. Finally, if successful, it will invoke the success callback, which in this case
|
|
|
|
is the <literal>loadData()</literal> method.
|
|
|
|
</para>
|
2014-04-30 13:38:02 +00:00
|
|
|
|
|
|
|
<para>
|
|
|
|
To refresh the token if it's expired call the <literal>updateToken</literal> method. This method returns a promise
|
|
|
|
object which can be used to invoke a function on success or failure. This method can be used to wrap functions
|
|
|
|
that should only be called with a valid token. For example the following method will refresh the token if it
|
|
|
|
expires within 30 seconds, and then invoke the specified function. If the token is valid for more than 30 seconds it
|
|
|
|
will just call the specified function.
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
keycloak.updateToken(30).success(function() {
|
|
|
|
// send request with valid token
|
|
|
|
}).error(function() {
|
|
|
|
alert('failed to refresh token');
|
|
|
|
);
|
|
|
|
]]></programlisting>
|
|
|
|
</para>
|
|
|
|
|
2014-05-14 16:46:19 +00:00
|
|
|
<section>
|
|
|
|
<title>Session status iframe</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
By default the JavaScript adapter creates a non-visible iframe that is used to detect if a single-sign out has occured.
|
|
|
|
This does not require any network traffic, instead the status is retrieved from a special status cookie. This feature can be disabled
|
|
|
|
by setting <literal>checkLoginIframe: false</literal> in the options passed to the <literal>init</literal>
|
|
|
|
method.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
2014-04-30 13:38:02 +00:00
|
|
|
<section>
|
|
|
|
<title>JavaScript Adapter reference</title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Constructor</title>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
new Keycloak();
|
|
|
|
new Keycloak('http://localhost/keycloak.json');
|
|
|
|
new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' });
|
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Properties</title>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>authenticated - true if the user is authenticated</listitem>
|
|
|
|
<listitem>token - the base64 encoded token that can be sent in the <literal>Authorization</literal> header in requests to services</listitem>
|
|
|
|
<listitem>tokenParsed - the parsed token</listitem>
|
|
|
|
<listitem>subject - the user id</listitem>
|
|
|
|
<listitem>idToken - the id token if claims is enabled for the application, null otherwise</listitem>
|
|
|
|
<listitem>realmAccess - the realm roles associated with the token</listitem>
|
|
|
|
<listitem>resourceAccess - the resource roles assocaited with the token</listitem>
|
|
|
|
<listitem>refreshToken - the base64 encoded token that can be used to retrieve a new token</listitem>
|
|
|
|
<listitem>refreshTokenParsed - the parsed refresh token</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Methods</title>
|
|
|
|
|
2014-05-14 16:46:19 +00:00
|
|
|
<simplesect>
|
|
|
|
<title>init(options)</title>
|
|
|
|
|
|
|
|
<para>Called to initialize the adapter.</para>
|
|
|
|
<para>Options is an Object, where:
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>onLoad - specifies an action to do on load, can be either 'login-required' or 'check-sso'</listitem>
|
|
|
|
<listitem>token - set an initial value for the token</listitem>
|
|
|
|
<listitem>refreshToken - set an initial value for the refresh token</listitem>
|
|
|
|
<listitem>checkLoginIframe - set to enable/disable monitoring login state (default is true)</listitem>
|
|
|
|
<listitem>checkLoginIframeInterval - set the interval to check login state (default is 5 seconds)</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
<para>Returns promise to set functions to be invoked on success or error.</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>login(options)</title>
|
|
|
|
|
|
|
|
<para>Redirects to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
|
|
|
|
<para>Options is an Object, where:
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
|
|
|
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
|
2014-07-30 13:27:14 +00:00
|
|
|
<listitem>loginHint - used to pre-fill the username/email field on the login form</listitem>
|
2014-05-14 16:46:19 +00:00
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
</simplesect>
|
|
|
|
<simplesect>
|
|
|
|
<title>createLoginUrl(options)</title>
|
|
|
|
|
|
|
|
<para>Returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
|
|
|
|
<para>Options is an Object, where:
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
|
|
|
|
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>logout(options)</title>
|
|
|
|
|
|
|
|
<para>Redirects to logout</para>
|
|
|
|
<para>Options is an Object, where:
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>createLogoutUrl(options)</title>
|
|
|
|
|
|
|
|
<para>Returns logout out</para>
|
|
|
|
<para>Options is an Object, where:
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>accountManagement()</title>
|
|
|
|
|
|
|
|
<para>Redirects to account management</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>createAccountUrl()</title>
|
|
|
|
|
|
|
|
<para>Returns the url to account management</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>hasRealmRole(role)</title>
|
|
|
|
|
|
|
|
<para>Returns true if the token has the given realm role</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>hasResourceRole(role, resource)</title>
|
|
|
|
|
|
|
|
<para>Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>loadUserProfile()</title>
|
|
|
|
|
|
|
|
<para>Loads the users profile</para>
|
|
|
|
|
|
|
|
<para>Returns promise to set functions to be invoked on success or error.</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>isTokenExpired(minValidity)</title>
|
|
|
|
|
|
|
|
<para>Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)</para>
|
|
|
|
</simplesect>
|
|
|
|
|
|
|
|
<simplesect>
|
|
|
|
<title>updateToken(minValidity)</title>
|
|
|
|
|
|
|
|
<para>If the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used) the token is refreshed.
|
|
|
|
If the session status iframe is enabled, the session status is also checked.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>Returns promise to set functions that can be invoked if the token is still valid, or if the token is no longer valid. For example:</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
keycloak.updateToken(5).success(function(refreshed) {
|
|
|
|
if (refreshed) {
|
|
|
|
alert('token was successfully refreshed');
|
|
|
|
} else {
|
|
|
|
alert('token is still valid');
|
|
|
|
}
|
|
|
|
}).error(function() {
|
|
|
|
alert('failed to refresh the token, or the session has expired');
|
|
|
|
});
|
|
|
|
]]></programlisting>
|
|
|
|
|
|
|
|
</simplesect>
|
2014-04-30 13:38:02 +00:00
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Callback Events</title>
|
|
|
|
|
|
|
|
<para>The adapter supports setting callback listeners for certain events. For example:
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
keycloak.onAuthSuccess = function() { alert('authenticated'); }
|
|
|
|
]]></programlisting>
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>onReady(authenticated) - called when the adapter is initialized</listitem>
|
|
|
|
<listitem>onAuthSuccess - called when a user is successfully authenticated</listitem>
|
|
|
|
<listitem>onAuthError - called if there was an error during authentication</listitem>
|
|
|
|
<listitem>onAuthRefreshSuccess - called when the token is refreshed</listitem>
|
|
|
|
<listitem>onAuthRefreshError - called if there was an error while trying to refresh the token</listitem>
|
2014-05-14 16:46:19 +00:00
|
|
|
<listitem>onAuthLogout - called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode)</listitem>
|
2014-04-30 13:38:02 +00:00
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
</section>
|
2014-02-25 00:58:54 +00:00
|
|
|
</section>
|