verify identity after oauth, closes #242

This commit is contained in:
spencerwooo
2022-01-08 16:47:33 +08:00
parent e82680f2a7
commit 9f349602ee
5 changed files with 75 additions and 31 deletions
+1 -1
View File
@@ -4,6 +4,6 @@
"redirectUri": "http://localhost",
"authApi": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"driveApi": "https://graph.microsoft.com/v1.0/me/drive",
"scope": "Files.Read.All Files.ReadWrite.All offline_access",
"scope": "user.read files.read.all offline_access",
"directLink": "https://public.dm.files.1drv.com"
}
+1
View File
@@ -1,4 +1,5 @@
{
"userPrincipalName": "spencer.woo@outlook.com",
"icon": "/icons/128.png",
"title": "Spencer's OneDrive",
"baseDirectory": "/Public",
+2
View File
@@ -23,6 +23,7 @@ import {
faCheckCircle,
} from '@fortawesome/free-regular-svg-icons'
import {
faCheck,
faPlus,
faMinus,
faCopy as faCopySolid,
@@ -89,6 +90,7 @@ library.add(
faExclamationCircle,
faExclamationTriangle,
faHome,
faCheck,
faCheckCircle,
...iconList
)
+42 -28
View File
@@ -1,4 +1,3 @@
import axios from 'axios'
import Head from 'next/head'
import Image from 'next/image'
import { useRouter } from 'next/router'
@@ -9,7 +8,7 @@ import siteConfig from '../../config/site.json'
import Navbar from '../../components/Navbar'
import Footer from '../../components/Footer'
import { obfuscateToken, requestTokenWithAuthCode } from '../../utils/oAuthHandler'
import { getAuthPersonInfo, requestTokenWithAuthCode, sendTokenToServer } from '../../utils/oAuthHandler'
import { LoadingIcon } from '../../components/Loading'
export default function OAuthStep3({ accessToken, expiryTime, refreshToken, error, description, errorUri }) {
@@ -27,48 +26,59 @@ export default function OAuthStep3({ accessToken, expiryTime, refreshToken, erro
}, [expiryTimeLeft])
const [buttonContent, setButtonContent] = useState(
<>
<div>
<span>Store tokens</span> <FontAwesomeIcon icon="key" />
</>
</div>
)
const [buttonError, setButtonError] = useState(false)
const sendAuthTokensToServer = async () => {
setButtonError(false)
setButtonContent(
<>
<div>
<span>Storing tokens</span> <LoadingIcon className="animate-spin w-4 h-4 ml-1 inline" />
</>
</div>
)
await axios
.post(
'/api',
{
obfuscatedAccessToken: obfuscateToken(accessToken),
accessTokenExpiry: parseInt(expiryTime),
obfuscatedRefreshToken: obfuscateToken(refreshToken),
},
{
headers: {
'Content-Type': 'application/json',
},
}
// verify identity of the authenticated user with the Microsoft Graph API
const { data, status } = await getAuthPersonInfo(accessToken)
if (status !== 200) {
setButtonError(true)
setButtonContent(
<div>
<span>Error validating identify, restart</span> <FontAwesomeIcon icon="exclamation-circle" />
</div>
)
.then(_ => {
setButtonContent(
<>
<span>Stored! Going home...</span> <FontAwesomeIcon icon="check" />
</>
)
return
}
if (data.userPrincipalName !== siteConfig.userPrincipalName) {
setButtonError(true)
setButtonContent(
<div>
<span>Do not pretend to be the site owner</span> <FontAwesomeIcon icon="exclamation-circle" />
</div>
)
return
}
await sendTokenToServer(accessToken, refreshToken, expiryTime)
.then(() => {
setButtonError(false)
setButtonContent(
<div>
<span>Stored! Going home...</span> <FontAwesomeIcon icon="check" />
</div>
)
setTimeout(() => {
router.push('/')
}, 2000)
})
.catch(_ => {
setButtonError(true)
setButtonContent(
<>
<div>
<span>Error storing the token</span> <FontAwesomeIcon icon="exclamation-circle" />
</>
</div>
)
})
}
@@ -168,7 +178,11 @@ export default function OAuthStep3({ accessToken, expiryTime, refreshToken, erro
<div className="text-right mb-2 mt-6">
<button
className="text-white bg-gradient-to-br from-green-500 to-teal-300 hover:bg-gradient-to-bl focus:ring-4 focus:ring-green-200 dark:focus:ring-green-800 font-medium rounded-lg text-sm px-4 py-2.5 text-center disabled:cursor-not-allowed disabled:grayscale"
className={`text-white bg-gradient-to-br hover:bg-gradient-to-bl focus:ring-4 font-medium rounded-lg text-sm px-4 py-2.5 text-center ${
buttonError
? 'from-red-500 to-orange-400 focus:ring-red-200 dark:focus:ring-red-800'
: 'from-green-500 to-teal-300 focus:ring-green-200 dark:focus:ring-green-800'
}`}
onClick={sendAuthTokensToServer}
>
{buttonContent}
+29 -2
View File
@@ -19,7 +19,7 @@ export function revealObfuscatedToken(obfuscated: string): string {
// Generate the Microsoft OAuth 2.0 authorization URL, used for requesting the authorisation code
export function generateAuthorisationUrl(): string {
const { clientId, redirectUri, authApi } = apiConfig
const { clientId, redirectUri, authApi, scope } = apiConfig
const authUrl = authApi.replace('/token', '/authorize')
// Construct URL parameters for OAuth2
@@ -27,7 +27,7 @@ export function generateAuthorisationUrl(): string {
params.append('client_id', clientId)
params.append('redirect_uri', redirectUri)
params.append('response_type', 'code')
params.append('scope', 'files.readwrite offline_access')
params.append('scope', scope)
params.append('response_mode', 'query')
return `${authUrl}?${params.toString()}`
@@ -82,3 +82,30 @@ export async function requestTokenWithAuthCode(
return { error, errorDescription: error_description, errorUri: error_uri }
})
}
// Verify the identity of the user with the access token and compare it with the userPrincipalName
// in the Microsoft Graph API. If the userPrincipalName matches, proceed with token storing.
export async function getAuthPersonInfo(accessToken: string) {
const profileApi = apiConfig.driveApi.replace('/drive', '')
return axios.get(profileApi, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
}
export async function sendTokenToServer(accessToken: string, refreshToken: string, expiryTime: string) {
return await axios.post(
'/api',
{
obfuscatedAccessToken: obfuscateToken(accessToken),
accessTokenExpiry: parseInt(expiryTime),
obfuscatedRefreshToken: obfuscateToken(refreshToken),
},
{
headers: {
'Content-Type': 'application/json',
},
}
)
}