import { put, takeLatest, all } from 'redux-saga/effects'; import { LOGOUT, LOGIN_REQUEST, HANDLE_OAUTH2_CALLBACK_REQUEST, handleOAuth2CallbackAction, HANDLE_OAUTH2_CALLBACK_FAILURE, handleOAuth2CallbackSuccess, parseIdTokenAction, parseIdToken, PARSE_ID_TOKEN_REQUEST, PARSE_ID_TOKEN_FAILURE, parseIdTokenSuccess, setCurrentUser, LOGIN_FAILURE, } from '../actions/auth'; import { createLoginSession, LoginSession, createAccessTokenRequest, saveAccessGrant, saveLoginSessionState, getSavedLoginSessionState, getLogoutURL, getSavedAccessGrant, clearAccessGrant } from '../../util/auth'; import qs from 'qs'; import { UnauthorizedError } from '../../util/daddy'; import jwtDecode from 'jwt-decode'; import { IdToken } from '../../types/idToken'; export function* authRootSaga() { yield all([ takeLatest(LOGIN_REQUEST, loginSaga), takeLatest(LOGOUT, logoutSaga), takeLatest(HANDLE_OAUTH2_CALLBACK_REQUEST, handleOAuth2CallbackSaga), takeLatest(PARSE_ID_TOKEN_REQUEST, parseIDTokenSaga), ]); } export function* loginSaga() { try { const loginSession: LoginSession = yield createLoginSession(); console.log('Code verifier is ', loginSession.verifier); console.log('State is ', loginSession.state); saveLoginSessionState(loginSession.verifier, loginSession.state); window.location.replace(loginSession.redirectUrl); } catch(err) { yield put({ type: LOGIN_FAILURE, err }); } } export function* logoutSaga() { const accessGrant = getSavedAccessGrant(); const logoutURL = getLogoutURL(accessGrant.id_token); clearAccessGrant(); window.location.replace(logoutURL); } export function* handleOAuth2CallbackSaga({ search }: handleOAuth2CallbackAction) { const query = search.substring(1); const params = qs.parse(query); const loginSession = getSavedLoginSessionState(); console.log('Stored state verifier is', loginSession.state); if (loginSession.state !== params.state) { yield put({ type: HANDLE_OAUTH2_CALLBACK_FAILURE, err: new Error("Invalid state") }); return; } console.log('Stored code verifier is', loginSession.verifier); console.log('Authorization code is', params.code); const req = createAccessTokenRequest(params.code as string, loginSession.verifier); let grant; try { grant = yield fetch(req.url, { method: "POST", body: req.data }) .then(res => { if (res.status === 401) return Promise.reject(new UnauthorizedError()); return res; }) .then(res => res.json()); } catch(err) { yield put({ type: HANDLE_OAUTH2_CALLBACK_FAILURE, err }); return; } console.log("Access grant is", grant); saveAccessGrant(grant); yield put(handleOAuth2CallbackSuccess(grant)); yield put(parseIdToken(grant.id_token)); }; export function* parseIDTokenSaga({ rawIdToken }: parseIdTokenAction) { let idToken: IdToken; try { idToken = jwtDecode(rawIdToken); } catch(err) { yield put({ type: PARSE_ID_TOKEN_FAILURE, err }); return; } yield put(parseIdTokenSuccess(idToken)); yield put(setCurrentUser(idToken.email)); };