Merge branch 'master' of ssh://moonshot.suchdamage.org/srv/git/gssweb
[gssweb.git] / navigator.gssweb.js
1 console.log("Loading navigator.gssweb.js - #2");
2
3
4 var GSSWeb = (function () {
5   function GSSWeb(config) {
6     this.serverPath = config.serverPath;
7     this.credential = config.credential;
8     this.appTag = config.appTag || "GSSWeb-" + navigator.generateNonce();
9     this.error = config.error || function (err) {
10       console.warn(err);
11     };
12     this.success = config.success;
13
14     this.version = "0.0.1";
15     this.authenticationState = false;
16     this.context = "";
17     this.serverToken = "";
18     this.clientCred = "";
19     this.xhr = new XMLHttpRequest();
20     this.xhr.open("POST", this.serverPath, true);
21     this.xhr.setRequestHeader(
22       'Content-Type', 
23       'application/x-www-form-urlencoded'
24     );
25     this.xhr.onreadystatechange = this.recvTokenFromServer.bind(this);
26
27     this.gss = new navigator.gss_eap({
28       appTag: this.appTag,
29       error: this.handleGSSError.bind(this)
30     });
31   }
32   /* What to invoke when the underlying GSS library 
33    * has a problem.
34    */
35   GSSWeb.prototype.handleGSSError = 
36     function (major, minor, errMsg, appTag) {
37       this.error(errMsg, appTag);
38     };
39
40   /* The basic authenticate function.
41    * Takes no arguments, as all should be supplied
42    * on the base object.
43    */
44   GSSWeb.prototype.authenticate = function () {
45     /* Error checking */
46     // Ensure that the callbacks exist well
47     if ( "function" != typeof(this.error) )
48     {
49       // I can't even call my error function!  All that can be done
50       // is throw an error.
51       throw("Error function not supplied to navigator.gssweb object!");
52     }
53     if ( "function" != typeof(this.success) )
54     {
55       // OK, so we have an error function; use it.
56       this.error(
57         "Success function not supplied to navigator.gssweb object!"
58       );
59     }
60     if ( ! this.presentString(this.serverPath) )
61     {
62       this.error("Server path not supplied to navigator.gssweb object!");
63     }
64
65     
66     /* Setup */
67     this.nonce = navigator.generateNonce();
68
69     // Start off the cascade by getting the
70     // GSS name of the server
71     this.authGetServerName();
72   };
73
74   GSSWeb.prototype.authGetServerName = function () {
75     this.gss.import_name({
76       name: "HTTP@" + window.location.host,
77       success: this.authReceiveServerName.bind(this)
78     });
79   };
80   GSSWeb.prototype.authReceiveServerName = function (data, appTag) {
81     this.serverName = data.gss_name;
82
83     /* Either move on to acquire_cred because we have been
84     * supplied a credential, or move on to init_sec_context
85     * when we have not.
86     */
87     if ( this.presentString(this.credential) )
88       this.authGetClientName();
89     else
90       this.authInitSecContext();
91   };
92
93   GSSWeb.prototype.authGetClientName = function () {
94     this.gss.import_name({
95       name: this.credential,
96       success: this.authReceiveClientName.bind(this)
97     });
98   };
99   GSSWeb.prototype.authReceiveClientName = function (data, appTag) {
100     this.clientName = data.gss_name;
101
102     // Next up: Get the local credential
103     this.authAcquireCred();
104   };
105
106   GSSWeb.prototype.authAcquireCred = function () {
107     this.gss.acquire_cred({
108       desired_name: this.clientName,
109       cred_usage: 1,
110       success: this.authReceiveClientCred.bind(this)
111     });
112   };
113   GSSWeb.prototype.authReceiveClientCred =
114     function (cred, actual_mechs, lifetime_rec) {
115       this.clientCred = cred;
116
117       this.authInitSecContext();
118     };
119
120   GSSWeb.prototype.authInitSecContext = function () {
121     var params = {
122       target_name: this.serverName,
123       success: this.sendTokenToServer.bind(this)
124     };
125
126     if ("" != this.clientCred) {
127       params.cred_handle = this.clientCred;
128     }
129     if ("" != this.serverToken) {
130       params.input_token = this.serverToken;
131     }
132     if ("" != this.context) {
133       params.context_handle = this.context;
134     }
135
136     this.gss.init_sec_context(params);
137   };
138
139   GSSWeb.prototype.sendTokenToServer = 
140     function (data, 
141               app_tag) {
142     this.clientToken = data.output_token;
143     this.context = data.context_handle;
144
145     var msg = "nonce=" + this.nonce +
146                "&token=" + encodeURIComponent(this.clientToken);
147     this.xhr.send(msg);
148   };
149
150   GSSWeb.prototype.recvTokenFromServer = function () {
151     // Only care when we're ready
152     if (this.xhr.readyState != 4) {
153       return;
154     }
155
156     var serverResponse = JSON.parse(this.xhr.responseText);
157     this.serverToken = serverResponse.gssweb.token;
158     switch (this.xhr.status) {
159       case 200:
160         // Finished!
161         var decoded = window.atob(serverResponse.application.data);
162         this.authenticationState = true;
163         this.success(
164           decoded,
165           serverResponse.application["content-type"],
166           this.appTag
167         );
168         break;
169       case 401:
170         // Continue needed
171         this.authInitSecContext();
172         break;
173       default:
174         // We have some server-reported error
175         this.error(
176           window.location.host + 
177           " reported an error; aborting",
178           this.appTag
179         );
180
181         // Destroy the GSS context.
182         this.context = undefined;
183         return;
184     }
185   };
186   
187   /*************************************
188    * Utility methods
189    *************************************/
190   // return true if the variable is a non-empty string
191   GSSWeb.prototype.presentString = function(str)
192   {
193     return(
194       "string" == typeof(str) &&
195       "" != str
196     );
197   }
198   
199   return GSSWeb;
200 })();
201
202 navigator.gssweb = GSSWeb;