X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F06_net%2F10_XOAuth2.js;h=b881ec29ce1c98ff95e11ec06796c7e7f8bd2189;hb=9e04e2a1221a92960a74e550bc3fd809caf3cd1c;hp=55c40eaa7d6fe16c7a9b1fca375e3920cde30473;hpb=8e63f506d14490e852fa557d326ca91f9cdd3baf;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/06_net/10_XOAuth2.js b/0.6.x/js/06_net/10_XOAuth2.js index 55c40ea..b881ec2 100644 --- a/0.6.x/js/06_net/10_XOAuth2.js +++ b/0.6.x/js/06_net/10_XOAuth2.js @@ -1,6 +1,6 @@ -var X_NET_OAUTH2_detection = new Function( 'w', 'try{return w.location.search}catch(e){}' ), - X_NET_OAUTH2_authorizationWindow, +//{+oauth2"OAuth2 サービスの定義"(OAuth2外部サービスを定義し、認可プロセス・xhrの署名を自動化します)[+xhr] +var X_NET_OAUTH2_authorizationWindow, X_NET_OAUTH2_authorizationTimerID; /** @@ -15,6 +15,7 @@ var X_NET_OAUTH2_detection = new Function( 'w', 'try{return w.location.sear * * original : * oauth2.js , + * https://github.com/ox-it/javascript-oauth2/blob/master/oauth2/oauth2.js * * @alias X.OAuth2 * @class OAuth2 サービスを定義し接続状況をモニタする。適宜にトークンのアップデートなどを行う @@ -26,8 +27,10 @@ oauth2 = X.OAuth2({ 'clientSecret' : 'xxxxxxxx', 'authorizeEndpoint' : 'https://accounts.google.com/o/oauth2/auth', 'tokenEndpoint' : 'https://accounts.google.com/o/oauth2/token', - 'redirectURI' : X.URL.cleanup( document.location.href ), // 専用の軽量ページを用意してもよいが、現在のアドレスでも可能 + 'redirectURI' : X.URL.cleanup( document.location.href ), // 専用の軽量ページを用意してもよいが、現在のアドレスでも可能, gif は? 'scopes' : [ 'https://www.googleapis.com/auth/blogger' ], + 'refreshMargin' : 300000, + // canuse 'authorizeWindowWidth' : 500, 'authorizeWindowHeight' : 500 }).listen( [ X.Event.NEED_AUTH, X.Event.CANCELED, X.Event.SUCCESS, X.Event.ERROR, X.Event.PROGRESS ], updateOAuth2State ); @@ -48,22 +51,29 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( /** @lends OAuth2.prototype */ { 'Constructor' : function( obj ){ - obj = X_Object_clone( obj ); + var expires_at; + + obj = X_Object_copy( obj ); + obj[ 'refreshMargin' ] = obj[ 'refreshMargin' ] || 300000; X_Pair_create( this, obj ); - if( _getAccessToken( this ) ){ - obj.oauth2State = 4; - this[ 'asyncDispatch' ]( X_EVENT_SUCCESS ); + obj.onAuthError = X_NET_OAUTH2_onXHR401Error; + obj.updateRequest = X_NET_OAUTH2_updateRequest; + + if( X_OAuth2_getAccessToken( this ) && ( expires_at = X_OAuth2_getAccessTokenExpiry( this ) ) ){ + if( expires_at < X_Timer_now() + obj[ 'refreshMargin' ] ){ // 寿命が5分を切った + this[ 'refreshToken' ](); + } else { + obj.oauth2State = 4; + this[ 'asyncDispatch' ]( X_EVENT_SUCCESS ); + }; } else { this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); - } - - obj.onAuthError = X_NET_OAUTH2_onXHR401Error; - obj.updateRequest = X_NET_OAUTH2_updateRequest; + }; - // TODO canUse - // TODO kill の cancel + // TODO canUse gadgetProxy + this[ 'listen' ]( [ X_EVENT_KILL_INSTANCE, X_EVENT_SUCCESS, X_EVENT_ERROR, X_EVENT_NEED_AUTH ], X_NET_OAUTH2_handleEvent ); }, /** @@ -85,6 +95,8 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( * 認可用 window をポップアップする。ポップアップブロックが働かないように必ず pointer event 内で呼ぶこと。 */ 'requestAuth' : function(){ + var url, w, h; + // TODO pointer event 内か?チェック // 二つ以上の popup を作らない if( X_NET_OAUTH2_authorizationWindow ) return; @@ -92,19 +104,24 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( if( pair.net || pair.oauth2State ) return; + url = pair[ 'authorizeEndpoint' ]; + w = pair[ 'authorizeWindowWidth' ] || 500; + h = pair[ 'authorizeWindowHeight' ] || 500; + X_NET_OAUTH2_authorizationWindow = window.open( - pair[ 'authorizeEndpoint' ] + '?' + X_URL_objToParam( + X_URL_create( url, { 'response_type' : 'code', 'client_id' : pair[ 'clientID' ], 'redirect_uri' : pair[ 'redirectURI' ], - 'scope' : ( pair[ 'scopes' ] || []).join(' ') + 'scope' : ( pair[ 'scopes' ] || [] ).join(' ') } - ), 'oauthauthorize', - 'width=' + pair[ 'authorizeWindowWidth' ] - + ',height=' + pair[ 'authorizeWindowHeight' ] - + ',left=' + (screen.width - pair[ 'authorizeWindowWidth' ] ) / 2 - + ',top=' + (screen.height - pair[ 'authorizeWindowHeight' ] ) / 2 + ), + 'oauthauthorize', + 'width=' + w + + ',height=' + h + + ',left=' + ( screen.width - w ) / 2 + + ',top=' + ( screen.height - h ) / 2 + ',menubar=no,toolbar=no'); X_NET_OAUTH2_authorizationTimerID = X_Timer_add( 333, 0, this, X_Net_OAuth2_detectAuthPopup ); @@ -125,9 +142,19 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( delete pair.net; }; + if( pair.oauth2State !== 1 ){ + return; + }; + // http://kojikoji75.hatenablog.com/entry/2013/12/15/223839 - X_NET_OAUTH2_authorizationWindow && X_NET_OAUTH2_authorizationWindow.open( 'about:blank', '_self' ).close(); - X_NET_OAUTH2_authorizationWindow = null; + if( X_NET_OAUTH2_authorizationWindow ){ + if( 9 < X_UA[ 'IEHost' ] ){ + X_NET_OAUTH2_authorizationWindow.close(); + } else { + X_NET_OAUTH2_authorizationWindow.open( 'about:blank', '_self' ).close(); + }; + X_NET_OAUTH2_authorizationWindow = null; + }; X_NET_OAUTH2_authorizationTimerID && X_Timer_remove( X_NET_OAUTH2_authorizationTimerID ); X_NET_OAUTH2_authorizationTimerID = 0; @@ -139,16 +166,22 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( * アクセストークンのリフレッシュ。 */ 'refreshToken' : function(){ - /* TODO 自動リフレッシュ - * var expires_at = this._getAccessTokenExpiry(); - if (expires_at && Date.now() + millis > expires_at) - this._refreshAccessToken({replay: false}); - */ + var pair = X_Pair_get( this ), + refreshToken = X_OAuth2_getRefreshToken( this ); - pair = X_Pair_get( this ); + if( !refreshToken ){ + pair.oauth2State = 0; + this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); + return; + }; if( pair.net ) return; + if( pair.refreshTimerID ){ + X_Timer_remove( pair.refreshTimerID ); + delete pair.refreshTimerID; + }; + pair.oauth2State = 3; pair.net = X.Net( { @@ -157,14 +190,14 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( 'client_id' : pair[ 'clientID' ], 'client_secret' : pair[ 'clientSecret' ], 'grant_type' : 'refresh_token', - 'refresh_token' : _getRefreshToken( this ) + 'refresh_token' : refreshToken }), 'dataType' : 'json', 'headers' : { 'Accept' : 'application/json', 'Content-Type' : 'application/x-www-form-urlencoded' }, - 'test' : 'gadget' + 'test' : 'gadget' // canuse } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_Net_OAuth2_responceHandler ); this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to refresh token.' } ); @@ -172,6 +205,27 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( } ); +function X_NET_OAUTH2_handleEvent( e ){ + var pair = X_Pair_get( this ); + + switch( e.type ){ + case X_EVENT_KILL_INSTANCE : + this[ 'cancelAuth' ](); + + case X_EVENT_ERROR : + case X_EVENT_NEED_AUTH : + pair.refreshTimerID && X_Timer_remove( pair.refreshTimerID ); + break; + + case X_EVENT_SUCCESS : + pair.refreshTimerID && X_Timer_remove( pair.refreshTimerID ); + if( X_OAuth2_getRefreshToken( this ) ){ + // 自動リフレッシュ + pair.refreshTimerID = X_Timer_once( X_OAuth2_getAccessTokenExpiry( this ) - X_Timer_now() - pair[ 'refreshMargin' ], this, this[ 'refreshToken' ] ); + }; + }; +}; + function X_Net_OAuth2_detectAuthPopup(){ var closed, search, pair = X_Pair_get( this ); @@ -181,11 +235,15 @@ function X_Net_OAuth2_detectAuthPopup(){ this[ 'asyncDispatch' ]( X_EVENT_CANCELED ); } else - if( search = X_NET_OAUTH2_detection( X_NET_OAUTH2_authorizationWindow ) ){ + if( search = X_Script_try( X_Object_find, [ X_NET_OAUTH2_authorizationWindow, 'location>search' ] ) ){ pair = X_Pair_get( this ); - pair.code = X_URL_ParamToObj( search.slice( 1 ) )[ 'code' ]; + pair.code = X_URL_paramToObj( search.slice( 1 ) )[ 'code' ]; - X_NET_OAUTH2_authorizationWindow.open( 'about:blank', '_self' ).close(); + if( 9 < X_UA[ 'IEHost' ] ){ + X_NET_OAUTH2_authorizationWindow.close(); + } else { + X_NET_OAUTH2_authorizationWindow.open( 'about:blank', '_self' ).close(); + }; closed = true; X_Net_OAuth2_authorizationCode( this, pair ); @@ -198,7 +256,7 @@ function X_Net_OAuth2_detectAuthPopup(){ X_NET_OAUTH2_authorizationWindow = null; X_NET_OAUTH2_authorizationTimerID = 0; - return X_Callback_UN_LISTEN; + return X_CALLBACK_UN_LISTEN; }; }; @@ -222,7 +280,7 @@ function X_Net_OAuth2_authorizationCode( oauth2, pair ){ }; function X_Net_OAuth2_responceHandler( e ){ - var data = e.data, + var data = e.response, pair = X_Pair_get( this ), isRefresh = pair.oauth2State === 3; @@ -231,30 +289,36 @@ function X_Net_OAuth2_responceHandler( e ){ switch( e.type ){ case X_EVENT_SUCCESS : if( isRefresh && data.error ){ - _removeRefreshToken( this ); + X_OAuth2_removeRefreshToken( this ); pair.oauth2State = 0; - this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' + data.error, data : data } ); + this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' } ); this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); return; } else if( data.error ){ pair.oauth2State = 0; - this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Get new access token error.' + data.error, data : data } ); + this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Get new access token error.' } ); this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); return; }; - _setAccessToken( this, data[ 'access_token' ] || '' ); - _setRefreshToken( this, data[ 'refresh_token' ] || '' ); + X_OAuth2_setAccessToken( this, data[ 'access_token' ] || '' ); + ( !isRefresh || data[ 'refresh_token' ] ) && X_OAuth2_setRefreshToken( this, data[ 'refresh_token' ] || '' ); if( data[ 'expires_in' ] ){ - _setAccessTokenExpiry( this, X_Timer_now() + data[ 'expires_in' ] * 1000 ); + X_OAuth2_setAccessTokenExpiry( this, X_Timer_now() + data[ 'expires_in' ] * 1000 ); } else - if( _getAccessTokenExpiry( this ) ){ - _removeAccessTokenExpiry( this ); + if( X_OAuth2_getAccessTokenExpiry( this ) ){ + X_OAuth2_removeAccessTokenExpiry( this ); }; pair.oauth2State = 4; + + if( pair.lazyRequests && pair.lazyRequests.length ){ + //X_NET_QUEUE_LIST.push.apply( X_NET_QUEUE_LIST, pair.lazyRequests ); + //pair.lazyRequests.length = 0; + }; + this[ 'asyncDispatch' ]( { type : X_EVENT_SUCCESS, message : isRefresh ? 'Refresh access token success.' : 'Get new access token success.' } ); break; @@ -263,15 +327,15 @@ function X_Net_OAuth2_responceHandler( e ){ // other error, not auth pair.oauth2State = 0; this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' } ); - _removeRefreshToken( this ); + X_OAuth2_removeRefreshToken( this ); this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); } else - if( _getAuthMechanism( this ) === 'param' ){ + if( X_OAuth2_getAuthMechanism( this ) === 'param' ){ pair.oauth2State = 0; this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'network-error' } ); } else { pair.oauth2State = 0; - _setAuthMechanism( this, 'param' ); + X_OAuth2_setAuthMechanism( this, 'param' ); this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Refresh access token failed. retry header -> param. ' } ); // retry X_Net_OAuth2_authorizationCode( this, pair ); @@ -283,40 +347,38 @@ function X_Net_OAuth2_responceHandler( e ){ function X_NET_OAUTH2_onXHR401Error( oauth2, e ){ var pair = this, headers = e[ 'headers' ], - xhr, bearerParams, headersExposed = false; + bearerParams, headersExposed = false; - if( _getAuthMechanism( oauth2 ) !== 'param' ){ - xhr = X_NET_currentWrapper[ '_rawObject' ]; - headersExposed = !X_Net_XHR_createXDR || !!headers; // this is a hack for Firefox and IE + if( X_OAuth2_getAuthMechanism( oauth2 ) !== 'param' ){ + headersExposed = !X_NET_currentWrapper.isXDR || !!headers; // this is a hack for Firefox and IE bearerParams = headersExposed && ( headers[ 'WWW-Authenticate' ] || headers[ 'www-authenticate' ] ); X_Type_isArray( bearerParams ) && ( bearerParams = bearerParams.join( '\n' ) ); }; // http://d.hatena.ne.jp/ritou/20110402/1301679908 - if ( bearerParams && bearerParams.indexOf( ' error=' ) === -1 ) { + if ( bearerParams && bearerParams.indexOf( ' error=' ) === -1 ) { // bearerParams.error == undefined pair.oauth2State = 0; oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); } else - if ((( bearerParams && bearerParams.indexOf( 'invalid_token' ) !== -1 ) || !headersExposed) && _getRefreshToken( oauth2 ) ) { - _removeAccessToken( oauth2 ); // It doesn't work any more. + if ((( bearerParams && bearerParams.indexOf( 'invalid_token' ) !== -1 ) || !headersExposed) && X_OAuth2_getRefreshToken( oauth2 ) ) { + X_OAuth2_removeAccessToken( oauth2 ); // It doesn't work any more. pair.oauth2State = 3; oauth2[ 'refreshToken' ](); } else { - //if (!headersExposed && !_getRefreshToken( oauth2 )) { - _removeAccessToken( oauth2 ); // It doesn't work any more. + X_OAuth2_removeAccessToken( oauth2 ); // It doesn't work any more. pair.oauth2State = 0; oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); }; }; function X_NET_OAUTH2_updateRequest( oauth2, request ){ - var token = _getAccessToken( oauth2 ), - mechanism = _getAuthMechanism( oauth2 ), + var token = X_OAuth2_getAccessToken( oauth2 ), + mechanism = X_OAuth2_getAuthMechanism( oauth2 ), url = request[ 'url' ], headers; if( token && mechanism === 'param' ){ - request[ 'url' ] = url + ((url.indexOf('?') !== -1) ? '&' : '?') + 'bearer_token=' + encodeURIComponent( token ); + request[ 'url' ] = X_URL_create( url, { 'bearer_token' : encodeURIComponent( token ) } ); }; if( token && ( !mechanism || mechanism === 'header' ) ){ @@ -325,25 +387,25 @@ function X_NET_OAUTH2_updateRequest( oauth2, request ){ }; }; -function _getAccessToken( that ){ return updateLocalStorage( '', that, 'accessToken' ); } -function _getRefreshToken( that){ return updateLocalStorage( '', that, 'refreshToken' ); } -function _getAccessTokenExpiry( that ){ return updateLocalStorage( '', that, 'tokenExpiry' ); } -function _getAuthMechanism( that ){ +function X_OAuth2_getAccessToken( that ){ return X_OAuth2_updateLocalStorage( '', that, 'accessToken' ); } +function X_OAuth2_getRefreshToken( that ){ return X_OAuth2_updateLocalStorage( '', that, 'refreshToken' ); } +function X_OAuth2_getAccessTokenExpiry( that ){ return parseFloat( X_OAuth2_updateLocalStorage( '', that, 'tokenExpiry' ) ) || 0; } +function X_OAuth2_getAuthMechanism( that ){ // TODO use gadget | flash ... // IE's XDomainRequest doesn't support sending headers, so don't try. - return X_Net_XHR_createXDR ? 'param' : updateLocalStorage( '', that, 'AuthMechanism' ); + return ( X_NET_currentWrapper === X_XHR ) && X_XHR_createXDR ? 'param' : X_OAuth2_updateLocalStorage( '', that, 'AuthMechanism' ); } -function _setAccessToken( that, value ){ updateLocalStorage( '+', that, 'accessToken' , value); } -function _setRefreshToken( that, value ){ updateLocalStorage( '+', that, 'refreshToken', value); } -function _setAccessTokenExpiry( that, value ){ updateLocalStorage( '+', that, 'tokenExpiry', value); } -function _setAuthMechanism( that, value ){ updateLocalStorage( '+', that, 'AuthMechanism', value); } +function X_OAuth2_setAccessToken( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'accessToken' , value); } +function X_OAuth2_setRefreshToken( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'refreshToken', value); } +function X_OAuth2_setAccessTokenExpiry( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'tokenExpiry', value); } +function X_OAuth2_setAuthMechanism( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'AuthMechanism', value); } -function _removeAccessToken( that ){ updateLocalStorage( '-', that, 'accessToken' ); } -function _removeRefreshToken( that ){ updateLocalStorage( '-', that, 'refreshToken' ); } -function _removeAccessTokenExpiry( that ){ updateLocalStorage( '-', that, 'tokenExpiry' ); } -function _removeAuthMechanism( that ){ updateLocalStorage( '-', that, 'AuthMechanism' ); } +function X_OAuth2_removeAccessToken( that ){ X_OAuth2_updateLocalStorage( '-', that, 'accessToken' ); } +function X_OAuth2_removeRefreshToken( that ){ X_OAuth2_updateLocalStorage( '-', that, 'refreshToken' ); } +function X_OAuth2_removeAccessTokenExpiry( that ){ X_OAuth2_updateLocalStorage( '-', that, 'tokenExpiry' ); } +function X_OAuth2_removeAuthMechanism( that ){ X_OAuth2_updateLocalStorage( '-', that, 'AuthMechanism' ); } -function updateLocalStorage( cmd, that, name, value ){ +function X_OAuth2_updateLocalStorage( cmd, that, name, value ){ var action = cmd === '+' ? 'setItem' : cmd === '-' ? 'removeItem' : 'getItem', pair; @@ -362,3 +424,4 @@ function updateLocalStorage( cmd, that, name, value ){ return pair[ name ]; }; +//}+oauth2 \ No newline at end of file