import { buildQueryString, extend } from '../util'
import { getAppKey, KAKAO_AGENT, processRules, UA, URL } from '../common'
import eventObserver from '../eventObserver'
import Poller from '../poller'
import rules from './rules'
import * as authCommon from './common'
import { checkAuthorize } from './ajax'
const poller = new Poller(1000, 600); // 10 min timeout
/**
* 사용자가 앱에 로그인할 수 있도록 인가 코드를 요청하는 함수입니다. 인가 코드를 받을 수 있는 서버 개발이 필요합니다.
* @function authorize
* @param {Object} settings 인가 코드 요청과 관련된 설정을 key/value로 전달합니다.
* @param {String} [settings.redirectUri] 인가 코드를 받을 URI
* @param {String} [settings.state] 인가 코드 요청과 응답 과정에서 유지할 수 있는 파라미터
* @param {String} [settings.scope] 추가 동의 받을 항목의 키 ex) "account_email,gender"
* @param {Boolean} [settings.throughTalk=true] 간편 로그인 사용 여부
* @param {String} [settings.prompts] 인가 코드 요청 시 추가 상호작용을 요청하고자 할 때 전달하는 파라미터 ex) "login": 다른 계정으로 로그인
* @see {@link https://developers.kakao.com/tool/demo/login/login|데모 보러가기}
* @memberof Kakao.Auth
*/
export function authorize(settings) {
settings = processRules(settings, rules.authorize, 'Auth.authorize')
if (settings.autoLogin && !/KAKAOTALK/i.test(UA.ua)) {
handleResponse(settings, {
error: 'auto_login',
error_description: 'NOT_SUPPORTED_BROWSER',
})
return false
}
const authTranId = getTranId()
const baseAuthParams = extend({},
authCommon.makeAuthParams(settings),
authCommon.makeAuthExtraParams(settings),
{
redirect_uri: settings.redirectUri || URL.redirectUri,
response_type: 'code',
auth_tran_id: authTranId,
},
)
const webAuthParams = extend({},
baseAuthParams,
{
ka: KAKAO_AGENT,
is_popup: settings.isPopup,
},
)
const isEasyLogin = isSupportEasyLogin(settings)
const isSupportSyncplugin = isTalkChannelHome(settings)
const webAuthUrl = authCommon.makeAuthUrl(webAuthParams)
const loginUrl = isEasyLogin ? makeEasyLoginUrl(settings, baseAuthParams, webAuthUrl) : webAuthUrl
let popup = null
if (isSupportSyncplugin) {
executeSyncpluginScheme(baseAuthParams)
} else if (settings.isPopup) {
popup = authCommon.openLoginPopup(loginUrl)
} else {
location.href = loginUrl
}
if (isEasyLogin || isSupportSyncplugin || settings.isPopup) {
poller.start(
() => {
const params = {
client_id: getAppKey(),
auth_tran_id: authTranId,
ka: KAKAO_AGENT,
}
checkAuthorize(
`${URL.authDomain}/apiweb/code.json?${buildQueryString(params)}`,
httpResp => {
const isValidResp = onResponse(settings, httpResp)
if (isValidResp) {
poller.stop()
popup && popup.close && popup.close()
}
if(!isEasyLogin && popup && popup.closed) {
poller.stop()
}
},
)
},
() => {
handleResponse(settings, {
error: 'timeout',
error_description: 'LOGIN_TIMEOUT',
})
},
)
}
eventObserver.dispatch('LOGIN_START')
}
function isSupportEasyLogin(settings) {
const isNotInAppBrowser = (UA.os.ios || UA.os.android) ? !/KAKAOTALK/i.test(UA.ua) : false
const isNotAccountLogin = !settings.reauthenticate && settings.prompts !== 'login'
return isNotInAppBrowser && isNotAccountLogin && settings.throughTalk && !settings.autoLogin
}
function getTranId() {
const tranId = Math.random().toString(36).slice(2) + getAppKey() + Date.now().toString(36)
return tranId.slice(0, 60)
}
function onResponse(settings, httpResp) {
if (httpResp.status === 200 && httpResp.response) {
const resp = JSON.parse(httpResp.response)
if (resp.status === 'ok' && resp.code) {
handleResponse(settings, {
code: resp.code,
})
return true
} else if (resp.status === 'error' && (resp.error_code === '500' || resp.error_code === '600' || resp.error_code === '700')) {
handleResponse(settings, {
error: resp.error,
error_description: resp.error_description,
})
if (resp.error_code === '700') {
location.href = `${URL.authDomain}/error/network`
}
return true
}
}
return false
}
function handleResponse(settings, respObj) {
if (settings.state) {
respObj.state = settings.state
}
if (settings.redirectUri) {
location.href = `${settings.redirectUri}?${buildQueryString(respObj)}`
} else {
authCommon.runAuthCallback(settings, respObj)
}
}
function makeEasyLoginUrl(settings, baseAuthParams, fallbackUrl) {
const easyLoginAuthParams = extend({},
baseAuthParams,
{
is_popup: true,
},
)
// TODO: ./kakaotalk method를 활용하도록 리팩토링
const getAndroidLoginIntent = () => {
const intent = [
'intent:#Intent',
'action=com.kakao.talk.intent.action.CAPRI_LOGGED_IN_ACTIVITY',
'launchFlags=0x08880000',
`S.com.kakao.sdk.talk.appKey=${getAppKey()}`,
`S.com.kakao.sdk.talk.redirectUri=${easyLoginAuthParams.redirect_uri}`,
`S.com.kakao.sdk.talk.kaHeader=${KAKAO_AGENT}`,
`S.com.kakao.sdk.talk.extraparams=${encodeURIComponent(JSON.stringify(easyLoginAuthParams))}`,
]
if (settings.state) {
intent.push(`S.com.kakao.sdk.talk.state=${settings.state}`)
}
return intent.concat([
`S.browser_fallback_url=${encodeURIComponent(fallbackUrl)}`,
'end;'
]).join(';')
}
const getIosLoginUniversalLink = () => {
const iosLoginUrl = authCommon.makeAuthUrl(easyLoginAuthParams)
const iosFallbackUrl = settings.isPopup ? iosLoginUrl : fallbackUrl
const iosEasyLoginUrl = `${iosLoginUrl}&ka=${encodeURIComponent(KAKAO_AGENT)}`
const talkWebviewUrl = `${URL.talkInappScheme}?url=${encodeURIComponent(iosEasyLoginUrl)}`
return `${URL.universalKakaoLink}${encodeURIComponent(talkWebviewUrl)}&web=${encodeURIComponent(iosFallbackUrl)}`
}
return UA.os.android ? getAndroidLoginIntent() : getIosLoginUniversalLink()
}
function isTalkChannelHome(settings) {
return settings.throughSyncplugin && /KAKAOTALK/i.test(UA.ua) && /ch-home/i.test(UA.ua)
}
function executeSyncpluginScheme(baseAuthParams) {
const bizpluginParams = extend({},
baseAuthParams,
{
ka: KAKAO_AGENT,
is_popup: true,
approval_window_type: 'v4_bizplugin',
},
)
const query = encodeURIComponent(buildQueryString(bizpluginParams))
location.href = `${URL.talkSyncpluginScheme}&query=${query}`
}