From f98ed25ed4ffb95a9a6ee4b09269b60c8e9070a4 Mon Sep 17 00:00:00 2001 From: Daniel Cortes Date: Tue, 23 Jun 2020 18:55:42 -0400 Subject: [PATCH] Auth basico completado --- src/components/Auth.jsx | 76 +++++++++++++++-------------------- src/index.jsx | 1 - src/services/State.jsx | 10 ++++- src/services/auth_service.js | 78 +++++++++++++++++++++++++++++++++--- 4 files changed, 113 insertions(+), 52 deletions(-) diff --git a/src/components/Auth.jsx b/src/components/Auth.jsx index 1dc3ebf..f9b4958 100644 --- a/src/components/Auth.jsx +++ b/src/components/Auth.jsx @@ -1,64 +1,52 @@ -import React, {useState} from 'react'; +import React from 'react'; import {Redirect} from "react-router-dom"; import queryString from "query-string"; -import {get_auth, obtain_code} from "../services/auth_service"; +import {auth, logout} from "../services/auth_service"; import {useStateValue} from "../services/State"; -export const AuthCallback = (props) => { - return null; -} - export const AuthLogin = (props) => { - const [response, setResponse] = useState(null); const [context, dispatch] = useStateValue(); - if (window.localStorage.getItem('refresh_token')) { + if (context.user.auth) { + // El usuario ya esta logeado, no hay nada que hacer + props.history.push('/') } - if (!response) { - if (context.user.auth) { - return - } + const params = queryString.parse(props.location.search); + auth(params).then((response) => { + console.debug(response, window.localStorage); - if (!window.localStorage.getItem('code_verifier')) { - const {redirect, code_verifier} = obtain_code(); - window.localStorage.setItem('code_verifier', code_verifier); - window.location.href = redirect; + if(response.status === 'redirect_to_code'){ + // Se va o ya se redirigió hacia la pagina que obtiene el código, no hay nada que hacer. return null; + } else if (response.status === 'code_error') { + // Hubo un error al obtener el código + props.history.push('/error') + } else if (response.status === 'done') { + // Termino gud, refresh_token y expires debería estar en local storage y access_token + // debió ser retornado con la response + props.history.push('/') + dispatch({type: 'login', user:{auth: true, access_token: response.access_token}}); + } else { + // Ocurrió un error inesperado al hacer auth :c + props.history.push('/error') } + }) - const parsedParams = queryString.parse(props.location.search); - if (parsedParams.error) { - console.log(parsedParams.error); - window.localStorage.removeItem('code_verifier'); - return - } - - const code = parsedParams.code; - const code_verifier = window.localStorage.getItem('code_verifier'); - get_auth(code, code_verifier).then((response) => setResponse(response)); - - return null; - } else { - console.log(response); - - window.localStorage.removeItem('code_verifier'); - - const refresh = response.refresh_token; - const expires = new Date(new Date().getTime() + ((response.expires_in) * 1000)) - - window.localStorage.setItem('refresh_token', refresh); - window.localStorage.setItem('expires', expires); - - dispatch({type: 'login', user: {auth: true, access_token: response.access_token}}); - - return - } + return null; } export const AuthLogout = (props) => { const [context, dispatch] = useStateValue(); - dispatch({action: 'logout', user: {auth: false}}) + if (!context.user.auth) { + return + } + + logout(context.user.access_token).then(result => { + console.log(result); + }); + + dispatch({type: 'logout', user: {auth: false}}) return } diff --git a/src/index.jsx b/src/index.jsx index 9870bb3..023e9e4 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -59,7 +59,6 @@ const App = () => ( - diff --git a/src/services/State.jsx b/src/services/State.jsx index f3b6079..3f47b97 100644 --- a/src/services/State.jsx +++ b/src/services/State.jsx @@ -11,7 +11,13 @@ const reducer = (state, action) => { ...state, user: action.user } - default: return state; + case 'logout': + return { + ...state, + user: action.user + } + default: + return state; } }; @@ -20,7 +26,7 @@ export const StateContext = createContext(null); export const StateProvider = ({children}) => { return ( - + {children} ); diff --git a/src/services/auth_service.js b/src/services/auth_service.js index 56f61ac..816f1b3 100644 --- a/src/services/auth_service.js +++ b/src/services/auth_service.js @@ -12,7 +12,7 @@ const generate_challenge = () => { } } -export const obtain_code = () => { +export const redirect_to_code = () => { const challenge = generate_challenge() const params = { response_type: 'code', @@ -24,10 +24,10 @@ export const obtain_code = () => { }; const url = `${oauth_url}/authorize/?${new URLSearchParams(params).toString()}`; - return { - redirect: url, - code_verifier: challenge.code - } + window.localStorage.setItem('code_verifier', challenge.code); + window.location.href = url; + + return null; } export const get_auth = async (code, code_verifier) => { @@ -43,3 +43,71 @@ export const get_auth = async (code, code_verifier) => { return response.data } + +export const auth = async (params) => { + // Primera fase, obtener código + // Si es que no se tiene se tiene que redirigir a la pagina de oauth que entregara el código + // a la pagina de redirect como parámetro GET, este trabajo lo hace redirect y se volverá a + // ejecutar este método cuando se entre a esa ruta + if (!window.localStorage.getItem('code_verifier')) { + redirect_to_code(); + return {status: 'redirect_to_code'}; + } + + // Segunda fase, se llama a auth con los parámetros de la ruta. + // Estos parámetros puede contener un error, en ese caso se elimina el code_verifier del storage + // porque no sera util y fallo la request. + if (params.error) { + window.localStorage.clear() + return {status: 'code_error', value: params.error}; + } + + // Teniendo el código en los parámetros se intenta obtener el código de autorización. + const code = params.code; + const code_verifier = window.localStorage.getItem('code_verifier'); + const response = await get_auth(code, code_verifier) + + // Puede que la respuesta sea erronea por varias razones + if (response.error) { + return {status: response.error} + } + + // Una vez se tiene la respuesta se almacena el refresh_token y el expires_in + // en localstorage para ser utilizado mas tarde para renovar el access_token + window.localStorage.clear() + + const refresh = response.refresh_token; + const expires = new Date(new Date().getTime() + ((response.expires_in) * 1000)) + + window.localStorage.setItem('refresh_token', refresh); + window.localStorage.setItem('expires', expires); + + // Finalmente se retorna el access_token para ser utilizado en el estado de la app + return {status: 'done', access_token: response.access_token}; +} + +export const logout = async (access_token) => { + // Para hacer logout de un usuario es necesario eliminar el refresh token de su localStorage + // y se llama a revocar los tokens en el servidor oauth + const revoke_access_params = { + token: access_token, + client_id: client_id, + token_type_hint: 'access_token' + } + const revoke_refresh_params = { + token: window.localStorage.getItem('refresh_token'), + client_id: client_id, + token_type_hint: 'refresh_token' + } + + window.localStorage.clear(); + + const url = `${oauth_url}/revoke_token/`; + const response_access_revoke = await axios.post(url, new URLSearchParams(revoke_access_params)); + const response_refresh_revoke = await axios.post(url, new URLSearchParams(revoke_refresh_params)); + + return { + access_revoke: response_access_revoke, + refresh_revoke: response_refresh_revoke, + } +}