Made it possible to auto-authenticate oauth2 with clientid and secret

This commit is contained in:
frikky
2021-08-24 01:14:32 +02:00
parent 488139c914
commit e9419eb7a8
10 changed files with 389 additions and 572 deletions
+12 -1
View File
@@ -12,7 +12,7 @@ import { FixName } from "../views/Apps.jsx";
//
// Specifically used for UNSAVED workflows only?
const Workflow = (props) => {
const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, } = props
const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, setAuthenticationType, } = props
const [requiredActions, setRequiredActions] = React.useState([])
const [requiredVariables, setRequiredVariables] = React.useState([])
const [requiredTriggers, setRequiredTriggers] = React.useState([])
@@ -374,6 +374,17 @@ const Workflow = (props) => {
<CircularProgress />
:
<Button color="primary" variant="contained" onClick={() => {
setAuthenticationType(action.app.authentication.type === "oauth2" && action.app.authentication.redirect_uri !== undefined && action.app.authentication.redirect_uri !== null ?
{
"type": "oauth2",
"redirect_uri": action.app.authentication.redirect_uri,
"token_uri": action.app.authentication.token_uri,
"scope": action.app.authentication.scope,
} : {
"type": ""
}
)
setItemChanged(true)
setSelectedAction(action.action)
setSelectedApp(action.app)
+258
View File
@@ -0,0 +1,258 @@
import React, {useRef, useState, useEffect, useLayoutEffect} from 'react';
import { useTheme } from '@material-ui/core/styles';
import { v4 as uuidv4 } from 'uuid';
import { TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade } from '@material-ui/core';
import { LockOpen as LockOpenIcon } from '@material-ui/icons';
const AuthenticationOauth2 = (props) => {
const { saveWorkflow, selectedApp, workflow, selectedAction, authenticationType, getAppAuthentication, appAuthentication, setSelectedAction, setNewAppAuth, setAuthenticationModalOpen} = props;
const theme = useTheme();
//const [update, setUpdate] = React.useState("|")
const [clientId, setClientId] = React.useState("")
const [clientSecret, setClientSecret] = React.useState("")
const [buttonClicked, setButtonClicked] = React.useState(false)
const [authenticationOption, setAuthenticationOptions] = React.useState({
app: JSON.parse(JSON.stringify(selectedApp)),
fields: {},
label: "",
usage: [{
workflow_id: workflow.id,
}],
id: uuidv4(),
active: true,
})
const handleOauth2Request = (client_id, client_secret) => {
setButtonClicked(true)
//if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) {
// These are test credentials
//const client_id = "dae24316-4bec-4832-b660-4cba6dc2477b"
//const client_secret = "._Qu3EvYY-OW_D57uy79qwEo.32qD6.l0z"
const authentication_url = authenticationType.token_uri
const resources = "UserAuthenticationMethod.ReadWrite.All"
if (authenticationType.scope !== undefined && authenticationType.scope !== null) {
console.log("EDIT SCOPE!")
}
const redirectUri = `http://${window.location.host}/set_authentication`
const state = `workflow_id%3D${workflow.id}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`
const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}`
// &resource=https%3A%2F%2Fgraph.microsoft.com&
// FIXME: Awful, but works for prototyping
// How can we get a callback properly realtime?
// How can we properly try-catch without breaks on error?
try {
var newwin = window.open(url, "", "width=400,height=200")
console.log(newwin)
setTimeout(() => {
console.log(newwin)
console.log("CLOSED", newwin.closed)
}, 1000)
setTimeout(() => {
console.log(newwin)
console.log("CLOSED", newwin.closed)
if (newwin.closed) {
getAppAuthentication(true, true)
setTimeout(() => {
console.log("APPAUTH: ", appAuthentication)
//{selectedAction.authentication.map(data => {
setAuthenticationModalOpen(false)
saveWorkflow(workflow)
}, 1500)
}
}, 10000)
} catch (e) {
alert.error("Failed authentication - probably bad credentials. Try again")
setButtonClicked(false)
}
return
//do {
//} while (
}
if (selectedApp.authentication === undefined) {
return null
}
if (selectedApp.authentication.parameters === null || selectedApp.authentication.parameters === undefined || selectedApp.authentication.parameters.length === 0) {
return null
}
authenticationOption.app.actions = []
for (var key in selectedApp.authentication.parameters) {
if (authenticationOption.fields[selectedApp.authentication.parameters[key].name] === undefined) {
authenticationOption.fields[selectedApp.authentication.parameters[key].name] = ""
}
}
const handleSubmitCheck = () => {
console.log("NEW AUTH: ", authenticationOption)
if (authenticationOption.label.length === 0) {
authenticationOption.label = `Auth for ${selectedApp.name}`
//alert.info("Label can't be empty")
//return
}
// Automatically mapping fields that already exist (predefined).
// Warning if fields are NOT filled
for (var key in selectedApp.authentication.parameters) {
if (authenticationOption.fields[selectedApp.authentication.parameters[key].name].length === 0) {
if (selectedApp.authentication.parameters[key].value !== undefined && selectedApp.authentication.parameters[key].value !== null && selectedApp.authentication.parameters[key].value.length > 0) {
authenticationOption.fields[selectedApp.authentication.parameters[key].name] = selectedApp.authentication.parameters[key].value
} else {
if (selectedApp.authentication.parameters[key].schema.type === "bool") {
authenticationOption.fields[selectedApp.authentication.parameters[key].name] = "false"
} else {
alert.info("Field "+selectedApp.authentication.parameters[key].name+" can't be empty")
return
}
}
}
}
console.log("Action: ", selectedAction)
selectedAction.authentication_id = authenticationOption.id
selectedAction.selectedAuthentication = authenticationOption
if (selectedAction.authentication === undefined || selectedAction.authentication === null) {
selectedAction.authentication = [authenticationOption]
} else {
selectedAction.authentication.push(authenticationOption)
}
setSelectedAction(selectedAction)
var newAuthOption = JSON.parse(JSON.stringify(authenticationOption))
var newFields = []
for (const key in newAuthOption.fields) {
const value = newAuthOption.fields[key]
newFields.push({
key: key,
value: value,
})
}
console.log("FIELDS: ", newFields)
newAuthOption.fields = newFields
setNewAppAuth(newAuthOption)
//appAuthentication.push(newAuthOption)
//setAppAuthentication(appAuthentication)
//
//if (configureWorkflowModalOpen) {
// setSelectedAction({})
//}
//setUpdate(authenticationOption.id)
/*
{selectedAction.authentication.map(data => (
<MenuItem key={data.id} style={{backgroundColor: inputColor, color: "white"}} value={data}>
*/
}
if (authenticationOption.label === null || authenticationOption.label === undefined) {
authenticationOption.label = selectedApp.name+" authentication"
}
//console.log(
return (
<div>
<DialogContent>
<span style={{}}>
<b>Oauth2 requires a client ID and secret to authenticate. This is usually made in the remote system.</b>
<a target="_blank" rel="norefferer" href="https://shuffler.io/docs/apps#authentication" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more about Oauth2</a><div/>
</span>
{/*<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
InputProps={{
style:{
color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em",
},
}}
fullWidth
color="primary"
placeholder={"Auth july 2020"}
defaultValue={`Auth for ${selectedApp.name}`}
onChange={(event) => {
authenticationOption.label = event.target.value
}}
/>
<Divider style={{marginTop: 15, marginBottom: 15, backgroundColor: "rgb(91, 96, 100)"}}/>
*/}
<TextField
style={{marginTop: 20, backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
InputProps={{
style:{
color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em",
},
}}
fullWidth
color="primary"
placeholder={"Client ID"}
onChange={(event) => {
setClientId(event.target.value)
//authenticationOption.label = event.target.value
}}
/>
<TextField
style={{backgroundColor: theme.palette.inputColor, borderRadius: theme.palette.borderRadius,}}
InputProps={{
style:{
color: "white",
marginLeft: "5px",
maxWidth: "95%",
height: 50,
fontSize: "1em",
},
}}
fullWidth
color="primary"
placeholder={"Client Secret"}
onChange={(event) => {
setClientSecret(event.target.value)
//authenticationOption.label = event.target.value
}}
/>
<Button
style={{marginTop: 20, borderRadius: theme.palette.borderRadius}}
disabled={clientSecret.length === 0 || clientId.length === 0}
variant="outlined"
fullWidth
onClick={() => {
//setAuthenticationModalOpen(false)
handleOauth2Request(clientId, clientSecret)
}}
color="primary"
>
{buttonClicked ?
<CircularProgress style={{}} />
:
"Oauth2 request"
}
</Button>
</DialogContent>
</div>
)
}
export default AuthenticationOauth2
+9 -41
View File
@@ -1116,6 +1116,14 @@ const ParsedAction = (props) => {
// itemColor = "#ffeb3b"
//}
{/*<div style={{width: 17, height: 17, borderRadius: 17 / 2, backgroundColor: itemColor, marginRight: 10, marginTop: 2, marginTop: "auto", marginBottom: "auto",}}/>*/}
if (authenticationType.type === "oauth2" && data.configuration === true) {
return null
}
//&& authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) {
return (
<div key={data.name}>
<div style={{marginTop: 20, marginBottom: 0, display: "flex"}}>
@@ -1389,47 +1397,7 @@ const ParsedAction = (props) => {
<Tooltip color="primary" title={"Add authentication option"} placement="top">
<span>
<Button color="primary" style={{}} fullWidth variant="contained" onClick={() => {
console.log(authenticationType)
if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) {
// FIXME: Sending client secret and senitive info like this may not be ok.
const client_id = "dae24316-4bec-4832-b660-4cba6dc2477b"
const client_secret = "._Qu3EvYY-OW_D57uy79qwEo.32qD6.l0z"
const authentication_url = authenticationType.token_uri
const resources = "UserAuthenticationMethod.ReadWrite.All"
if (authenticationType.scope !== undefined && authenticationType.scope !== null) {
console.log("EDIT SCOPE!")
}
const redirectUri = `http://${window.location.host}/set_authentication`
const state = `workflow_id%3D${workflow.id}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}`
const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}`
// &resource=https%3A%2F%2Fgraph.microsoft.com&
// Awful, but works for prototyping
var newwin = window.open(url, "", "width=200,height=100")
console.log(newwin)
setTimeout(() => {
console.log(newwin)
console.log("CLOSED", newwin.closed)
}, 1000)
setTimeout(() => {
console.log(newwin)
console.log("CLOSED", newwin.closed)
if (newwin.closed) {
getAppAuthentication(true)
setTimeout(() => {
console.log("APPAUTH: ", appAuthentication)
}, 1500)
}
}, 10000)
return
//do {
//} while (
}
console.log(authenticationType)
setAuthenticationModalOpen(true)
}}>
+78 -5
View File
@@ -28,6 +28,7 @@ import { useAlert } from "react-alert";
import { validateJson, GetIconInfo } from "./Workflows.jsx";
import { GetParsedPaths } from "./Apps.jsx";
import ConfigureWorkflow from '../components/ConfigureWorkflow.jsx';
import AuthenticationOauth2 from "../components/Oauth2Auth.jsx";
import ParsedAction from '../components/ParsedAction.jsx';
import Scroll from 'react-scroll'
import { Element as ScrollElement, animateScroll as scroll, scrollSpy, scroller } from 'react-scroll'
@@ -404,7 +405,7 @@ const AngularWorkflow = (props) => {
if (!responseJson.success) {
alert.error("Failed to set app auth: "+responseJson.reason)
} else {
getAppAuthentication(true)
getAppAuthentication(true, false)
setAuthenticationModalOpen(false)
// Needs a refresh with the new authentication..
@@ -1128,7 +1129,7 @@ const AngularWorkflow = (props) => {
"http",
]
const getAppAuthentication = (reset) => {
const getAppAuthentication = (reset, updateAction) => {
fetch(globalUrl+"/api/v1/apps/authentication", {
method: 'GET',
headers: {
@@ -1154,6 +1155,8 @@ const AngularWorkflow = (props) => {
newauth.push(responseJson.data[key])
}
//{selectedAction.authentication.map(data => {
if (cy !== undefined) {
console.log("NEW AUTH = reset cy's onnodeselect")
@@ -1166,6 +1169,65 @@ const AngularWorkflow = (props) => {
setAppAuthentication(newauth)
setAuthLoaded(true)
if (updateAction === true) {
//console.log("Should update authentication for selectedAction!!")
console.log(responseJson)
if (selectedApp.authentication.required) {
//console.log("App requires auth!!")
// Setup auth here :)
const authenticationOptions = []
var findAuthId = ""
if (selectedAction.authentication_id !== null && selectedAction.authentication_id !== undefined && selectedAction.authentication_id.length > 0) {
findAuthId = selectedAction.authentication_id
}
var tmpAuth = JSON.parse(JSON.stringify(responseJson.data))
console.log("FOUND AUTH: ", tmpAuth)
//console.log("Checking authentication: ", tmpAuth)
var latest = 0
for (var key in tmpAuth) {
var item = tmpAuth[key]
const newfields = {}
for (var filterkey in item.fields) {
newfields[item.fields[filterkey].key] = item.fields[filterkey].value
}
item.fields = newfields
if (item.app.name === selectedApp.name) {
authenticationOptions.push(item)
// Always becoming the last one
if (item.edited > latest) {
latest = item.edited
selectedAction.selectedAuthentication = item
}
}
}
//if (item.id === findAuthId) {
// selectedAction.selectedAuthentication = item
//}
console.log("ACTION: ", selectedAction)
//console.log("OPTIONS: ", authenticationOptions)
selectedAction.authentication = authenticationOptions
//console.log("Authentication: ", authenticationOptions)
if (selectedAction.selectedAuthentication === null || selectedAction.selectedAuthentication === undefined || selectedAction.selectedAuthentication.length === "") {
selectedAction.selectedAuthentication = {}
}
setSelectedAction(selectedAction)
alert.info("Updated authentication for app?")
} else {
alert.info("No authentication to update")
}
}
} else {
setAuthLoaded(true)
//alert.error("Failed getting authentications")
@@ -1945,7 +2007,8 @@ const AngularWorkflow = (props) => {
setSelectedAction(curaction)
//return
} else {
console.log("AUTHENTICATION: ", curapp.authentication)
//console.log("AUTHENTICATION: ", curapp.authentication)
//console.log(curapp.authentication)
setAuthenticationType(curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null ?
{
"type": "oauth2",
@@ -5450,6 +5513,10 @@ const AngularWorkflow = (props) => {
setLocalFirstrequest(false)
}
const handleButtonRequest = () => {
}
const outlookButton =
<Button
fullWidth
@@ -8625,6 +8692,7 @@ const AngularWorkflow = (props) => {
: null
const AuthenticationData = (props) => {
const selectedApp = props.app
@@ -8883,12 +8951,13 @@ const AngularWorkflow = (props) => {
}}>
<CloseIcon />
</IconButton>
<ConfigureWorkflow theme={theme} globalUrl={globalUrl} workflow={workflow} setSelectedAction={setSelectedAction} setSelectedApp={setSelectedApp} setAuthenticationModalOpen={setAuthenticationModalOpen} appAuthentication={appAuthentication} selectedAction={selectedAction} apps={apps} setConfigureWorkflowModalOpen={setConfigureWorkflowModalOpen} saveWorkflow={saveWorkflow} newWebhook={newWebhook} submitSchedule={submitSchedule} referenceUrl={referenceUrl} isCloud={isCloud} />
<ConfigureWorkflow theme={theme} setAuthenticationType={setAuthenticationType} globalUrl={globalUrl} workflow={workflow} setSelectedAction={setSelectedAction} setSelectedApp={setSelectedApp} setAuthenticationModalOpen={setAuthenticationModalOpen} appAuthentication={appAuthentication} selectedAction={selectedAction} apps={apps} setConfigureWorkflowModalOpen={setConfigureWorkflowModalOpen} saveWorkflow={saveWorkflow} newWebhook={newWebhook} submitSchedule={submitSchedule} referenceUrl={referenceUrl} isCloud={isCloud} />
</Dialog>
: null
// This whole part is redundant. Made it part of Arguments instead.
//console.log("TYPE: ", authenticationType)
const authenticationModal = authenticationModalOpen ?
<Dialog
open={authenticationModalOpen}
@@ -8917,7 +8986,11 @@ const AngularWorkflow = (props) => {
<CloseIcon />
</IconButton>
<DialogTitle><div style={{color: "white"}}>Authentication for {selectedApp.name}</div></DialogTitle>
<AuthenticationData app={selectedApp} />
{authenticationType.type === "oauth2" ?
<AuthenticationOauth2 saveWorkflow={saveWorkflow} selectedApp={selectedApp} workflow={workflow} selectedAction={selectedAction} authenticationType={authenticationType} getAppAuthentication={getAppAuthentication} appAuthentication={appAuthentication} setSelectedAction={setSelectedAction} setNewAppAuth={setNewAppAuth} setAuthenticationModalOpen={setAuthenticationModalOpen} />
:
<AuthenticationData app={selectedApp} />
}
</Dialog> : null
//const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
+21 -19
View File
@@ -984,19 +984,19 @@ const Apps = (props) => {
</div>
{isCloud ? null :
<span>
<Tooltip title={"Reload apps locally"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
<Button
variant="outlined"
component="label"
color="primary"
style={{margin: 5, maxHeight: 50, marginTop: 10}}
onClick={() => {
hotloadApps()
}}
>
<CachedIcon />
</Button>
</Tooltip>
<Tooltip title={"Reload apps locally"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
<Button
variant="outlined"
component="label"
color="primary"
style={{margin: 5, maxHeight: 50, marginTop: 10}}
onClick={() => {
hotloadApps()
}}
>
<CachedIcon />
</Button>
</Tooltip>
<Tooltip title={"Download from Github"} style={{marginTop: "28px", width: "100%"}} aria-label={"Upload"}>
<Button
variant="outlined"
@@ -1012,7 +1012,7 @@ const Apps = (props) => {
</Button>
</Tooltip>
</span>
}
}
</div>
<div style={{height: 50}}>
<TextField
@@ -1575,11 +1575,13 @@ const Apps = (props) => {
<Button style={{borderRadius: "0px"}} onClick={() => setLoadAppsModalOpen(false)} color="primary">
Cancel
</Button>
<Button style={{borderRadius: "0px"}} disabled={openApi.length === 0 || !openApi.includes("http")} onClick={() => {
handleGithubValidation(true)
}} color="primary">
Force update
</Button>
{isCloud ? null :
<Button style={{borderRadius: "0px"}} disabled={openApi.length === 0 || !openApi.includes("http")} onClick={() => {
handleGithubValidation(true)
}} color="primary">
Force update
</Button>
}
<Button variant="outlined" style={{float: "left", borderRadius: "0px"}} disabled={openApi.length === 0 || !openApi.includes("http")} onClick={() => {
handleGithubValidation(false)
}} color="primary">