2 * The MIT License (MIT)
4 * Copyright (c) 2015 JISC
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 var GSSWeb = (function () {
27 function GSSWeb(config) {
28 this.serverPath = config.serverPath;
29 this.credential = config.credential;
30 this.appTag = config.appTag || "GSSWeb-" + navigator.generateNonce();
31 this.error = config.error || function (err) {
34 this.success = config.success;
36 this.version = "0.0.1";
37 this.authenticationState = false;
39 this.serverToken = "";
41 this.xhr = new XMLHttpRequest();
43 this.gss = new navigator.gss({
45 error: this.handleGSSError.bind(this)
48 /* What to invoke when the underlying GSS library
51 GSSWeb.prototype.handleGSSError =
52 function (major, minor, errMsg, appTag) {
53 this.error(errMsg, appTag);
56 /* The basic authenticate function.
57 * Takes no arguments, as all should be supplied
60 GSSWeb.prototype.authenticate = function () {
62 // Ensure that the callbacks exist well
63 if ( "function" != typeof(this.error) )
65 // I can't even call my error function! All that can be done
67 throw("Error function not supplied to navigator.gssweb object!");
69 if ( "function" != typeof(this.success) )
71 // OK, so we have an error function; use it.
73 "Success function not supplied to navigator.gssweb object!"
76 if ( ! this.presentString(this.serverPath) )
78 this.error("Server path not supplied to navigator.gssweb object!");
83 this.nonce = navigator.generateNonce();
85 // Start off the cascade by getting the
86 // GSS name of the server
87 this.authGetServerName();
90 GSSWeb.prototype.authGetServerName = function () {
91 this.gss.import_name({
92 name: "HTTP@" + window.location.hostname,
93 success: this.authReceiveServerName.bind(this)
96 GSSWeb.prototype.authReceiveServerName = function (data, appTag) {
97 this.serverName = data.gss_name;
99 /* Either move on to acquire_cred because we have been
100 * supplied a credential, or move on to init_sec_context
103 if ( this.presentString(this.credential) )
104 this.authGetClientName();
106 this.authInitSecContext();
109 GSSWeb.prototype.authGetClientName = function () {
110 this.gss.import_name({
111 name: this.credential,
112 success: this.authReceiveClientName.bind(this)
115 GSSWeb.prototype.authReceiveClientName = function (data, appTag) {
116 this.clientName = data.gss_name;
118 // Next up: Get the local credential
119 this.authAcquireCred();
122 GSSWeb.prototype.authAcquireCred = function () {
123 this.gss.acquire_cred({
124 desired_name: this.clientName,
126 success: this.authReceiveClientCred.bind(this)
129 GSSWeb.prototype.authReceiveClientCred =
130 function (cred, actual_mechs, lifetime_rec) {
131 this.clientCred = cred;
133 this.authInitSecContext();
136 GSSWeb.prototype.authInitSecContext = function () {
138 target_name: this.serverName,
139 success: this.sendTokenToServer.bind(this)
142 if ("" != this.clientCred) {
143 params.cred_handle = this.clientCred;
145 if ("" != this.serverToken) {
146 params.input_token = this.serverToken;
148 if ("" != this.context) {
149 params.context_handle = this.context;
152 this.gss.init_sec_context(params);
155 GSSWeb.prototype.sendTokenToServer =
158 this.clientToken = data.output_token;
159 this.context = data.context_handle;
161 var msg = "nonce=" + this.nonce +
162 "&token=" + encodeURIComponent(this.clientToken);
164 this.xhr.open("POST", this.serverPath, true);
166 this.xhr.setRequestHeader(
168 'application/x-www-form-urlencoded'
170 this.xhr.onreadystatechange = this.recvTokenFromServer.bind(this);
175 GSSWeb.prototype.recvTokenFromServer = function () {
176 // Only care when we're ready
177 if (this.xhr.readyState != 4) {
181 switch (this.xhr.status) {
184 var serverResponse = JSON.parse(this.xhr.responseText);
185 var decoded = window.atob(serverResponse.application.data);
186 this.authenticationState = true;
189 serverResponse.application["content-type"],
195 var serverResponse = JSON.parse(this.xhr.responseText);
196 this.serverToken = serverResponse.gssweb.token;
197 this.authInitSecContext();
200 // We have some server-reported error
202 window.location.hostname +
203 " reported an error; aborting",
207 // Destroy the GSS context.
208 this.context = undefined;
213 /*************************************
215 *************************************/
216 // return true if the variable is a non-empty string
217 GSSWeb.prototype.presentString = function(str)
220 "string" == typeof(str) &&
228 navigator.gssweb = GSSWeb;