mirror of
https://github.com/zhigang1992/devhub.git
synced 2026-06-17 11:11:21 +08:00
Handle new appToken and githubToken
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import qs from 'qs'
|
||||
|
||||
export interface OAuthResponseData {
|
||||
access_token?: string
|
||||
app_token?: string
|
||||
code: string
|
||||
github_token?: string
|
||||
scope: string[]
|
||||
}
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user