3939
4040 < script src ='https://ajaxorg.github.io/ace-builds/src-noconflict/ace.js '> </ script >
4141
42+ <!--
4243 <script src='generate.js'></script>
44+ -->
4345
4446
4547 < script src ='https://libs.ext-code.com/js/dom/component/component.js '> </ script >
@@ -183,13 +185,16 @@ <h1 class=visually-hidden>generate access token from service account keyfile</h1
183185
184186
185187 < script >
186-
187- var encoder = new TextEncoder ( ) ;
188188
189-
189+
190190 var keyfile ;
191191 var token ;
192+
193+ var scope = 'https://www.googleapis.com/auth/devstorage.read_write' ;
194+
192195
196+ var btn = { } ;
197+
193198
194199 function initdom ( ) {
195200
@@ -199,234 +204,95 @@ <h1 class=visually-hidden>generate access token from service account keyfile</h1
199204 log . initdom ( ) ;
200205
201206
202- document . querySelector ( '[value=copy]' ) . onclick = e => {
207+ $ ( '[value=copy]' ) . onclick = btn . copy ;
208+ $ ( '[value=new]' ) . onclick = btn . new ;
203209
204- if ( ! token ) {
205- disp ( 'no token' ) ;
206- return ;
207- }
208- navigator . clipboard . writeText ( token ) ;
209- disp ( 'token copied' ) ;
210-
211- } //copy
212-
213-
214- document . querySelector ( '[value=new]' ) . onclick = async e => {
215210
216- if ( ! keyfile ) {
217- disp ( 'no service account file selected' ) ;
218- return ;
219- }
211+ file . onchange = onchange ;
220212
221213
222-
223- output . replaceChildren ( ) ;
224-
225- var json ;
226- ( { json, token} = await generate ( keyfile ) ) ;
214+
215+ } //initdom
216+
227217
218+ //:
219+
220+
221+ btn . copy = function ( ) {
222+
223+ if ( ! token ) {
224+ disp ( 'no token' ) ;
225+ return ;
226+ }
227+ navigator . clipboard . writeText ( token ) ;
228+ disp ( 'token copied' ) ;
228229
229- disp ( JSON . stringify ( json , null , 4 ) ) ;
230-
231- disp ( ) ;
232-
233- disp ( 'token' ) ;
234- disp ( token ) ;
235-
236- disp ( ) ;
237-
238- navigator . clipboard . writeText ( token ) ;
239- disp ( 'copied to clipboard' ) ;
240-
241-
242- } //new
230+ } //copy
231+
232+
233+ btn . new = async function ( ) {
243234
235+ if ( ! keyfile ) {
236+ disp ( 'no service account file' ) ;
237+ return ;
238+ }
239+ create ( ) ;
244240
245- file . onchange = async function ( ) {
246-
247- var blob = file . files [ 0 ] ;
248- var txt = await blob . text ( ) ;
249- keyfile = JSON . parse ( txt ) ;
250-
251-
252- output . replaceChildren ( ) ;
253-
254-
255- var { json, token} = await generate ( keyfile ) ;
241+ } //new
256242
257243
258- disp ( JSON . stringify ( json , null , 4 ) ) ;
259-
260- disp ( ) ;
261-
262- disp ( 'token' ) ;
263- disp ( token ) ;
264-
265- disp ( ) ;
266-
267- navigator . clipboard . writeText ( token ) ;
268- disp ( 'copied to clipboard' ) ;
269-
270-
271- } //onchange
272-
273-
274-
275- } //initdom
276-
244+ async function onchange ( ) {
245+
246+ var blob = file . files [ 0 ] ;
247+ var txt = await blob . text ( ) ;
248+ keyfile = JSON . parse ( txt ) ;
249+ create ( ) ;
250+
251+ } //onchange
252+
277253
278254 //:
279255
280256
281-
282- /*
283-
284-
285- async function generate(keyfile){
257+ async function create ( ) {
258+
259+ output . replaceChildren ( ) ;
286260
287-
288-
289- var clientEmail = keyfile.client_email;
290- var privateKeyPem = keyfile.private_key.replace(/\\n/g,'\n');
291- var scope = 'https://www.googleapis.com/auth/devstorage.read_write';
261+ gen ( ) ;
292262
293- var assertion = await buildJwtAssertion({clientEmail,privateKeyPem,scope});
294- var json = await exchangeForAccessToken(assertion);
295- var token = json.access_token;
263+ var { json, error} = await generate ( keyfile , scope ) ;
296264
265+ if ( error ) {
266+ disp . error ( error ) ;
267+ return ;
268+ }
297269
298- return {json,token};
299-
300-
301-
302- function base64url(input){
303-
304- let bytes;
305-
306- if(typeof input=='string'){
307- bytes = encoder.encode(input);
308- }else if(input instanceof ArrayBuffer){
309- bytes = new Uint8Array(input);
310- }else if(ArrayBuffer.isView(input)){
311- bytes = new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
312- }else{
313- throw new TypeError('Unsupported input');
314- }
315-
316- var bin = '';
317- for(var i=0;i<bytes.length;i++){
318-
319- bin += String.fromCharCode(bytes[i]);
320-
321- }//for
322-
323- var str = btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
324- return str;
325-
326- }//base64url
327-
328-
329- function pemToArrayBuffer(pem){
330-
331- const b64 = pem.replace(/-----BEGIN [^-]+-----/g, '')
332- .replace(/-----END [^-]+-----/g, '')
333- .replace(/\s+/g, '');
334- const raw = atob(b64);
335- const buf = new Uint8Array(raw.length);
336-
337- for(let i=0;i<raw.length;i++){
338-
339- buf[i] = raw.charCodeAt(i);
340-
341- }//fpr
342-
343- return buf.buffer;
344-
345- }//pem_buf
346-
347-
348- async function importPkcs8PrivateKey(pem) {
270+ token = json . access_token ;
271+
272+
349273
350- var keyData = pemToArrayBuffer(pem);
351- var key = await crypto.subtle.importKey('pkcs8',keyData,{name:'RSASSA-PKCS1-v1_5',hash:'SHA-256'},false,['sign']);
352- return key
353-
354- }//import
355-
356-
357- async function signRS256(privateKey, dataToSign) {
274+ disp . json ( json ) ;
358275
359- var dataBytes = encoder.encode(dataToSign);
360- var sig = await crypto.subtle.sign({name:'RSASSA-PKCS1-v1_5'},privateKey,dataBytes);
361- var uint8 = new Uint8Array(sig);
362- return uint8;
363-
364- }//sign
365-
366-
367- async function buildJwtAssertion({clientEmail,privateKeyPem,scope,aud='https://oauth2.googleapis.com/token'}){
276+ disp ( ) ;
368277
369- var key = await importPkcs8PrivateKey(privateKeyPem);
370-
371- var now = Math.floor(Date.now()/1000);
372- var header = {alg:'RS256',typ:'JWT'};
373- var payload = {
374- iss : clientEmail,
375- scope : Array.isArray(scope) ? scope.join(' ') : scope,
376- aud : aud,
377- iat : now,
378- exp : now+3600, // 1 hour max
379- };
380-
381- var encodedHeader = base64url(JSON.stringify(header));
382- var encodedPayload = base64url(JSON.stringify(payload));
383- var unsigned = `${encodedHeader}.${encodedPayload}`;
384-
385- var sig = await signRS256(key,unsigned);
386- var encodedSig = base64url(sig);
387- var str = `${unsigned}.${encodedSig}`;
388- return str
389-
390- }//build
391-
278+ disp ( 'token' ) ;
279+ disp ( token ) ;
392280
393- async function exchangeForAccessToken(assertion) {
281+ disp ( ) ;
394282
395- var url = 'https://oauth2.googleapis.com/token';
396- var body = new URLSearchParams({grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer',assertion});
397- var headers = {'Content-Type':'application/x-www-form-urlencoded'};
398-
399- var err;
400- try{
401-
402- var res = await fetch(url,{method:'post',headers,body});
403-
404- }//try
405- catch(err2){
406-
407- err = err2;
408-
409- }//catch
410- if(err){
411- console.error(err);
412- disp(err.toString());
413- return;
414- }
415-
416- if(!res.ok){
417- throw new Error(`Token exchange failed: ${res.status} ${res.statusText} ${await res.text()}`);
418- }
419- // { access_token, token_type, expires_in }
420- var json = await res.json();
421- return json;
422-
423- }//exchange
424-
283+ navigator . clipboard . writeText ( token ) ;
284+ disp ( 'copied to clipboard' ) ;
285+
286+ } //create
425287
426- }//generate
427-
288+
289+ function gen ( ) {
290+
291+ var txt = script . getvalue ( ) ;
292+ window . eval ( txt ) ;
293+
294+ } //gen
428295
429- */
430296
431297 function disp ( ) {
432298
@@ -439,8 +305,25 @@ <h1 class=visually-hidden>generate access token from service account keyfile</h1
439305 output . append ( div ) ;
440306
441307 output . scrollTop = output . scrollHeight ;
308+ return div ;
442309
443310 } //disp
311+
312+
313+ disp . error = function ( ) {
314+
315+ var div = disp . apply ( null , arguments ) ;
316+ div . style . color = 'red' ;
317+
318+ } //error
319+
320+
321+ disp . json = function ( v ) {
322+
323+ var str = JSON . stringify ( v , null , 4 ) ;
324+ disp ( str ) ;
325+
326+ } //json
444327
445328
446329
0 commit comments