Handle new appToken and githubToken

This commit is contained in:
Bruno Lemos
2018-11-26 20:17:31 -02:00
parent d00667d0ca
commit 8decebe868
9 changed files with 123 additions and 54 deletions

View File

@@ -1,8 +1,9 @@
import qs from 'qs'
export interface OAuthResponseData {
access_token?: string
app_token?: string
code: string
github_token?: string
scope: string[]
}

View File

@@ -21,17 +21,18 @@ export const listenForNextMessageData = (
!(
e &&
e.data &&
(e.data.oauth || (e.data.access_token || e.data.error))
(e.data.oauth ||
(e.data.app_token || e.data.github_token || e.data.error))
)
) {
return
}
const { access_token: accessToken, error } = e.data
const { app_token: appToken, github_token: githubToken, error } = e.data
window.removeEventListener('message', handleMessage)
if (accessToken && !error) resolve(e.data)
if (appToken && githubToken && !error) resolve(e.data)
else
reject(
new Error(typeof error === 'string' ? error : 'No token received'),

View File

@@ -24,7 +24,7 @@ export async function executeOAuth(scopes: string[]) {
if (typeof Browser.dismiss === 'function') Browser.dismiss()
if (!(params && params.access_token)) {
if (!(params && params.app_token && params.github_token)) {
throw new Error('Login failed: No access token received.')
}

View File

@@ -28,7 +28,7 @@ export async function executeOAuth(scopes: string[]) {
const data = await listenForNextMessageData(popup)
// console.log('[OAUTH] Received data:', data)
if (!(data && data.access_token)) {
if (!(data && data.app_token && data.github_token)) {
throw new Error('Login failed: No access token received.')
}

View File

@@ -4,11 +4,18 @@ import {
createErrorAction,
} from 'shared-core/dist/utils/helpers/redux'
export function loginRequest(payload: { token: string }) {
export function loginRequest(payload: {
appToken: string
githubToken: string
}) {
return createAction('LOGIN_REQUEST', payload)
}
export function loginSuccess(payload: { user: User }) {
export function loginSuccess(payload: {
appToken: string
githubToken: string
user: User
}) {
return createAction('LOGIN_SUCCESS', payload)
}

View File

@@ -1,39 +1,52 @@
import _ from 'lodash'
import { REHYDRATE } from 'redux-persist'
import { User } from 'shared-core/dist/types/graphql'
import { Reducer } from '../types'
export interface State {
appToken: string | null
error: object | null
githubToken: string | null
isLoggingIn: boolean
lastLoginAt: string | null
token: string
user: User | null
}
const initialState: State = {
appToken: null,
error: null,
githubToken: null,
isLoggingIn: false,
lastLoginAt: null,
token: '',
user: null,
}
export const authReducer: Reducer<State> = (state = initialState, action) => {
switch (action.type) {
case REHYDRATE as any:
return {
...(action.payload as any).auth,
..._.pick(initialState, ['error', 'isLoggingIn']),
}
case 'LOGIN_REQUEST':
return {
appToken: action.payload.appToken,
error: null,
githubToken: action.payload.githubToken,
isLoggingIn: true,
lastLoginAt: state.lastLoginAt,
token: action.payload.token,
user: state.user,
}
case 'LOGIN_SUCCESS':
return {
appToken: action.payload.appToken || state.appToken,
error: null,
githubToken: action.payload.githubToken || state.githubToken,
isLoggingIn: false,
lastLoginAt: new Date().toISOString(),
token: state.token,
user: action.payload.user,
}

View File

@@ -6,6 +6,7 @@ import {
ExtractActionFromActionCreator,
GitHubUser,
} from 'shared-core/dist/types'
import { User } from 'shared-core/dist/types/graphql'
import { GRAPHQL_ENDPOINT } from 'shared-core/dist/utils/constants'
import { fromGitHubUser } from '../../api/mappers/user'
import * as github from '../../libs/github'
@@ -13,47 +14,60 @@ import * as actions from '../actions'
import * as selectors from '../selectors'
function* onRehydrate() {
const token = yield select(selectors.tokenSelector)
if (token) yield put(actions.loginRequest({ token }))
const appToken = yield select(selectors.appTokenSelector)
const githubToken = yield select(selectors.githubTokenSelector)
if (!(appToken && githubToken)) return
yield put(actions.loginRequest({ appToken, githubToken }))
}
function* onLoginRequest(
action: ExtractActionFromActionCreator<typeof actions.loginRequest>,
) {
github.authenticate(action.payload.token || '')
try {
github.authenticate(action.payload.githubToken || '')
const response: AxiosResponse<{
data: { me: any }
data: {
login: {
appToken: string
githubToken: string
user: User | null
} | null
}
errors?: any[]
}> = yield axios.post(
GRAPHQL_ENDPOINT,
{
query: `query me {
me {
id
nodeId
login
name
avatarUrl
type
bio
publicGistsCount
publicReposCount
privateReposCount
privateGistsCount
followersCount
followingCount
ownedPrivateReposCount
isTwoFactorAuthenticationEnabled
createdAt
updatedAt
query: `query auth {
login {
appToken
githubToken
user {
id
nodeId
login
name
avatarUrl
type
bio
publicGistsCount
publicReposCount
privateReposCount
privateGistsCount
followersCount
followingCount
ownedPrivateReposCount
isTwoFactorAuthenticationEnabled
createdAt
updatedAt
}
}
}`,
},
{
headers: {
Authorization: `bearer ${action.payload.token}`,
Authorization: `bearer ${action.payload.appToken}`,
},
},
)
@@ -64,9 +78,28 @@ function* onLoginRequest(
throw { response }
}
if (!(data && data.me && data.me.id)) throw new Error('Invalid response')
if (
!(
data &&
data.login &&
data.login.appToken &&
data.login.githubToken &&
data.login.user &&
data.login.user.id
)
) {
throw new Error('Invalid response')
}
yield put(actions.loginSuccess({ user: data.me }))
github.authenticate(data.login.githubToken)
yield put(
actions.loginSuccess({
appToken: data.login.appToken,
githubToken: data.login.githubToken,
user: data.login.user,
}),
)
return
} catch (error) {
console.error(error.response)
@@ -74,12 +107,7 @@ function* onLoginRequest(
if (
error &&
error.response &&
(error.response.status === 401 ||
(error.response.data &&
Array.isArray(error.response.data.errors) &&
error.response.data.errors.some(
(e: any) => e.extensions && e.extensions.code === 'UNAUTHENTICATED',
)))
(error.response.status >= 200 || error.response.status < 500)
) {
yield put(actions.loginFailure(error.response.data))
return
@@ -92,12 +120,24 @@ function* onLoginRequest(
const user = fromGitHubUser(githubUser)
if (!(user && user.id && user.login)) throw new Error('Invalid response')
yield put(actions.loginSuccess({ user }))
yield put(
actions.loginSuccess({
appToken: action.payload.appToken,
githubToken: action.payload.githubToken,
user,
}),
)
} catch (error) {
yield put(actions.loginFailure(error))
}
}
function onLoginSuccess(
action: ExtractActionFromActionCreator<typeof actions.loginSuccess>,
) {
github.authenticate(action.payload.githubToken)
}
function* onLoginFailure(
action: ExtractActionFromActionCreator<typeof actions.loginFailure>,
) {
@@ -113,6 +153,7 @@ export function* authSagas() {
yield takeLatest(REHYDRATE, onRehydrate),
yield takeLatest('LOGIN_FAILURE', onLoginFailure),
yield takeLatest('LOGIN_REQUEST', onLoginRequest),
yield takeLatest('LOGIN_SUCCESS', onLoginSuccess),
yield takeLatest('LOGOUT', onLogout),
])
}

View File

@@ -6,7 +6,11 @@ export const errorSelector = (state: RootState) => s(state).error
export const isLoggingInSelector = (state: RootState) => s(state).isLoggingIn
export const tokenSelector = (state: RootState) => s(state).token
export const appTokenSelector = (state: RootState) => s(state).appToken
export const githubTokenSelector = (state: RootState) => s(state).githubToken
export const currentUserSelector = (state: RootState) =>
tokenSelector(state) ? s(state).user : undefined
appTokenSelector(state) && githubTokenSelector(state)
? s(state).user
: undefined

View File

@@ -85,7 +85,7 @@ const connectToStore = connect(
user: selectors.currentUserSelector(state),
}),
{
login: actions.loginRequest,
loginRequest: actions.loginRequest,
},
)
@@ -107,20 +107,22 @@ export class LoginScreenComponent extends PureComponent<
? ['user', 'repo', 'notifications', 'read:org']
: ['user', 'public_repo', 'notifications', 'read:org']
let token
let appToken
let githubToken
try {
const params = await executeOAuth(permissions)
if (!(params && params.access_token))
throw new Error('No token received.')
appToken = params && params.app_token
githubToken = params && params.github_token
token = params.access_token
if (!(appToken && githubToken)) throw new Error('No token received.')
} catch (e) {
console.error(e)
if (e.message === 'Canceled' || e.message === 'Timeout') return
alert(`Login failed. ${e || ''}`)
return
}
await this.props.login({ token })
await this.props.loginRequest({ appToken, githubToken })
}
loginWithGitHubPrivateAccess = () => this._loginWithGitHub('github.private')