X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F06_net%2F10_XOAuth2.js;h=5f23c6ced529fad16f6574e1d0cef31203a1e2cb;hb=9e607206c5510435fc1daeac93a19427ce33cc6f;hp=8bdd18dcbec74eaf5d3459e13c3059c065489cd3;hpb=8e74cf066ea48ec8cf34efb2b5e84725c10c813a;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 8bdd18d..5f23c6c 100644 --- a/0.6.x/js/06_net/10_XOAuth2.js +++ b/0.6.x/js/06_net/10_XOAuth2.js @@ -1,13 +1,12 @@ -//{+oauth2"OAuth2 サービスの定義"(OAuth2外部サービスを定義し、認可プロセス・xhrの署名を自動化します)[+xhr] -var X_NET_OAUTH2_detection = new Function( 'w', 'try{return w.location.search}catch(e){}' ), - X_NET_OAUTH2_authorizationWindow, - X_NET_OAUTH2_authorizationTimerID; +//{+oauth2"OAuth2 サービスの定義"(OAuth2外部サービスを定義し、認可プロセス・xhrの署名を自動化します)[+xhr,+window] +var X_OAUTH2_authWindow, + X_OAUTH2_authTimerID; /** * イベント *
- *
X.Event.NEED_AUTH
window を popup して認可を行う必要あり。ポインターイベント内で oauth2.requestAuth() を呼ぶ。 + *
X.Event.NEED_AUTH
window を popup して認可を行う必要あり。ポインターイベント内で oauth2.requestAuth() を呼ぶ。このイベントをキャンセルすると *
X.Event.CANCELED
認可 window が閉じられた。([x]等でウインドウが閉じられた、oauth2.cancelAuth() が呼ばれた) *
X.Event.SUCCESS
認可 window でユーザーが認可し、続いてコードの認可が済んだ。 *
X.Event.ERROR
コードの認可のエラー、リフレッシュトークンのエラー、ネットワークエラー @@ -16,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 サービスを定義し接続状況をモニタする。適宜にトークンのアップデートなどを行う @@ -27,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 ); @@ -51,15 +53,16 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( 'Constructor' : function( obj ){ var expires_at; - obj = X_Object_clone( obj ); + obj = X_Object_copy( obj ); + obj[ 'refreshMargin' ] = obj[ 'refreshMargin' ] || 300000; X_Pair_create( this, obj ); obj.onAuthError = X_NET_OAUTH2_onXHR401Error; obj.updateRequest = X_NET_OAUTH2_updateRequest; - if( _getAccessToken( this ) && ( expires_at = _getAccessTokenExpiry( this ) ) ){ - if( expires_at < X_Timer_now() + ( obj[ 'refreshMargin' ] || 300000 ) ){ // 寿命が5分を切った + 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; @@ -70,7 +73,7 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( }; // TODO canUse gadgetProxy - this[ 'listen' ]( [ X_EVENT_KILL_INSTANCE, X_EVENT_SUCCESS, X_EVENT_ERROR, X_EVENT_NEED_AUTH ], X_NET_OAUTH2_handleEvent ); + this[ 'listen' ]( [ X_EVENT_KILL_INSTANCE, X_EVENT_SUCCESS, X_EVENT_ERROR, X_EVENT_NEED_AUTH ], X_OAUTH2_handleEvent ); }, /** @@ -90,38 +93,53 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( /** * 認可用 window をポップアップする。ポップアップブロックが働かないように必ず pointer event 内で呼ぶこと。 + *
+ *
1 :
認可用 window がポップアップ中(自身) + *
2 :
コードを認可中 + *
3 :
トークンのリフレッシュ中 + *
4 :
接続 + *
5 :
他のOAuth2サービスの認可用 window がポップアップ中 + *
+ * @return {number} */ 'requestAuth' : function(){ - var url, w, h; + var e = X_EventDispatcher_CURRENT_EVENTS[ X_EventDispatcher_CURRENT_EVENTS.length - 1 ], + w, h, pair; + // TODO pointer event 内か?チェック + if( !e || !e[ 'pointerType' ] ){ + alert( 'タッチイベント以外での popup! ' + ( e ? e.type : '' ) ); + return; + }; + // 二つ以上の popup を作らない - if( X_NET_OAUTH2_authorizationWindow ) return; + if( X_OAUTH2_authWindow ) return; pair = X_Pair_get( this ); if( pair.net || pair.oauth2State ) return; - - url = pair[ 'authorizeEndpoint' ]; + w = pair[ 'authorizeWindowWidth' ] || 500; h = pair[ 'authorizeWindowHeight' ] || 500; + + X_OAUTH2_authWindow = X_Window( { + 'url' : X_URL_create( pair[ 'authorizeEndpoint' ], + { + 'response_type' : 'code', + 'client_id' : pair[ 'clientID' ], + 'redirect_uri' : pair[ 'redirectURI' ], + 'scope' : ( pair[ 'scopes' ] || [] ).join( ' ' ) + } + ), + 'name' : 'oauthauthorize', + 'params' : 'width=' + w + + ',height=' + h + + ',left=' + ( screen.width - w ) / 2 + + ',top=' + ( screen.height - h ) / 2 + + ',menubar=no,toolbar=no' + } )[ 'listen' ]( X_EVENT_UNLOAD, this, X_OAuth2_detectAuthPopup ); - X_NET_OAUTH2_authorizationWindow = window.open( - X_URL_create( url, - { - 'response_type' : 'code', - 'client_id' : pair[ 'clientID' ], - 'redirect_uri' : pair[ 'redirectURI' ], - 'scope' : ( pair[ 'scopes' ] || []).join(' ') - } - ), - '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 ); + X_OAUTH2_authTimerID = X_Timer_add( 333, 0, this, X_OAuth2_detectAuthPopup ); pair.oauth2State = 1; @@ -144,11 +162,13 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( }; // 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_OAUTH2_authWindow ){ + X_OAUTH2_authWindow[ 'kill' ](); + X_OAUTH2_authWindow = null; + }; - X_NET_OAUTH2_authorizationTimerID && X_Timer_remove( X_NET_OAUTH2_authorizationTimerID ); - X_NET_OAUTH2_authorizationTimerID = 0; + X_OAUTH2_authTimerID && X_Timer_remove( X_OAUTH2_authTimerID ); + X_OAUTH2_authTimerID = 0; this[ 'asyncDispatch' ]( X_EVENT_CANCELED ); }, @@ -157,7 +177,14 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( * アクセストークンのリフレッシュ。 */ 'refreshToken' : function(){ - var pair = X_Pair_get( this ); + var pair = X_Pair_get( this ), + refreshToken = X_OAuth2_getRefreshToken( this ); + + if( !refreshToken ){ + pair.oauth2State = 0; + this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); + return; + }; if( pair.net ) return; @@ -174,22 +201,22 @@ 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' - } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_Net_OAuth2_responceHandler ); + 'test' : 'gadget' // canuse + } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_OAuth2_responceHandler ); this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to refresh token.' } ); } } ); -function X_NET_OAUTH2_handleEvent( e ){ +function X_OAUTH2_handleEvent( e ){ var pair = X_Pair_get( this ); switch( e.type ){ @@ -203,44 +230,42 @@ function X_NET_OAUTH2_handleEvent( e ){ case X_EVENT_SUCCESS : pair.refreshTimerID && X_Timer_remove( pair.refreshTimerID ); - if( _getRefreshToken( this ) ){ + if( X_OAuth2_getRefreshToken( this ) ){ // 自動リフレッシュ - pair.refreshTimerID = X_Timer_once( _getAccessTokenExpiry( this ) - X_Timer_now() - pair[ 'refreshMargin' ], this, this[ 'refreshToken' ] ); + 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 ); - - if( X_NET_OAUTH2_authorizationWindow.closed ){ - pair.oauth2State = 0; - closed = true; +function X_OAuth2_detectAuthPopup( e ){ + var pair = X_Pair_get( this ), + status, search; + if( X_OAUTH2_authWindow[ 'closed' ]() ){ + status = 0; this[ 'asyncDispatch' ]( X_EVENT_CANCELED ); } else - if( search = X_NET_OAUTH2_detection( X_NET_OAUTH2_authorizationWindow ) ){ - pair = X_Pair_get( this ); - pair.code = X_URL_ParamToObj( search.slice( 1 ) )[ 'code' ]; - - X_NET_OAUTH2_authorizationWindow.open( 'about:blank', '_self' ).close(); - closed = true; - - X_Net_OAuth2_authorizationCode( this, pair ); - - pair.oauth2State = 2; + if( search = X_OAUTH2_authWindow[ 'find' ]( 'location>search' ) ){ + pair = X_Pair_get( this ); + pair.code = X_URL_paramToObj( search.slice( 1 ) )[ 'code' ]; + status = 2; + X_OAuth2_authorizationCode( this, pair ); this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Get code success, then authorization code.' } ); }; - if( closed ){ - X_NET_OAUTH2_authorizationWindow = null; - X_NET_OAUTH2_authorizationTimerID = 0; + if( 0 <= status ){ + pair = pair || X_Pair_get( this ); + pair.oauth2State = status; - return X_Callback_UN_LISTEN; + X_OAUTH2_authWindow[ 'kill' ](); + X_OAUTH2_authWindow = null; + X_OAUTH2_authTimerID = X_Timer_remove( X_OAUTH2_authTimerID ); + + return X_CALLBACK_UN_LISTEN; }; }; -function X_Net_OAuth2_authorizationCode( oauth2, pair ){ +function X_OAuth2_authorizationCode( oauth2, pair ){ pair.net = X.Net( { 'xhr' : pair[ 'tokenEndpoint' ], 'postdata' : X_URL_objToParam({ @@ -256,11 +281,11 @@ function X_Net_OAuth2_authorizationCode( oauth2, pair ){ 'Content-Type' : 'application/x-www-form-urlencoded' }, 'test' : 'gadget' - } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], oauth2, X_Net_OAuth2_responceHandler ); + } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], oauth2, X_OAuth2_responceHandler ); }; -function X_Net_OAuth2_responceHandler( e ){ - var data = e.data, +function X_OAuth2_responceHandler( e ){ + var data = e.response, pair = X_Pair_get( this ), isRefresh = pair.oauth2State === 3; @@ -269,30 +294,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; @@ -301,18 +332,18 @@ 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 ); + X_OAuth2_authorizationCode( this, pair ); }; break; }; @@ -321,35 +352,33 @@ 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; @@ -363,29 +392,29 @@ 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 parseInt( updateLocalStorage( '', that, 'tokenExpiry' ) ) || 0; } -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; - if( window.localStorage ){ + if( window.localStorage ){ // TODO http://qiita.com/narikei/items/f55fb9cb398beac52ea9 return window.localStorage[ action ]( X_Pair_get( that )[ 'clientID' ] + name, value ); };