Fixed some search issues and Oauth2 Application perms + JWT problems

This commit is contained in:
Frikky
2023-11-15 00:53:02 +01:00
parent f6c7939dda
commit df6d7f88de
4 changed files with 321 additions and 180 deletions
+104 -44
View File
@@ -93,10 +93,10 @@ const AuthenticationOauth2 = (props) => {
appAuthentication, appAuthentication,
setSelectedAction, setSelectedAction,
setNewAppAuth, setNewAppAuth,
isCloud, isCloud,
autoAuth, autoAuth,
authButtonOnly, authButtonOnly,
isLoggedIn, isLoggedIn,
} = props; } = props;
let navigate = useNavigate(); let navigate = useNavigate();
@@ -297,15 +297,74 @@ const AuthenticationOauth2 = (props) => {
const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => { const handleOauth2Request = (client_id, client_secret, oauth_url, scopes, admin_consent, prompt) => {
setButtonClicked(true);
//console.log("SCOPES: ", scopes); if ((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) {
console.log("No redirect URI found, and token URI found. Assuming client credentials flow and saving directly in the database")
// Find app.configuration=true fields in the app.paramters
var parsedFields = [{
"key": "client_id",
"value": client_id,
},
{
"key": "client_secret",
"value": client_secret,
},
{
"key": "scope",
"value": scopes.join(","),
},
{
"key": "token_uri",
"value": authenticationType.token_uri,
}]
// Not necessary yet to do something like this due to not showing the fields anyway
/*
if (selectedApp.parameters !== undefined && selectedApp.parameters !== null && selectedApp.parameters.length > 0) {
for (var i = 0; i < selectedApp.parameters.length; i++) {
if (selectedApp.parameters[i].configuration !== true) {
continue
}
console.log("Found configuration field: ", selectedApp.parameters[i].key, " with example: ", selectedApp.parameters[i].example)
parsedFields.push({
"key": selectedApp.parameters[i].key,
"value": selectedApp.parameters[i].example,
})
}
}
*/
const appAuthData = {
"label": "OAuth2 for " + selectedApp.name,
"app": {
"id": selectedApp.id,
"name": selectedApp.name,
"version": selectedApp.version,
"large_image": selectedApp.large_image,
},
"fields": parsedFields,
"type": "oauth2-app",
"reference_workflow": workflowId,
}
setNewAppAuth(appAuthData)
return
}
setButtonClicked(true);
//console.log("SCOPES: ", scopes);
client_id = client_id.trim() client_id = client_id.trim()
client_secret = client_secret.trim() client_secret = client_secret.trim()
oauth_url = oauth_url.trim() oauth_url = oauth_url.trim()
var resources = ""; var resources = "";
if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) { if (scopes !== undefined && (scopes !== null) & (scopes.length > 0)) {
console.log("IN scope 1") console.log("IN scope 1")
if (offlineAccess === true && !scopes.includes("offline_access")) { if (offlineAccess === true && !scopes.includes("offline_access")) {
@@ -324,26 +383,26 @@ const AuthenticationOauth2 = (props) => {
//console.log("AUTH: ", authenticationType) //console.log("AUTH: ", authenticationType)
//console.log("SCOPES2: ", resources) //console.log("SCOPES2: ", resources)
const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`; const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication`;
const workflowId = workflow !== undefined ? workflow.id : ""; const workflowId = workflow !== undefined ? workflow.id : "";
var state = `workflow_id%3D${workflowId}%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}`; var state = `workflow_id%3D${workflowId}%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}`;
// This is to make sure authorization can be handled WITHOUT being logged in, // This is to make sure authorization can be handled WITHOUT being logged in,
// kind of making it act like an api key // kind of making it act like an api key
// https://shuffler.io/authorization -> 3rd party integration auth // https://shuffler.io/authorization -> 3rd party integration auth
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const userAuth = urlParams.get("authorization"); const userAuth = urlParams.get("authorization");
if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) { if (userAuth !== undefined && userAuth !== null && userAuth.length > 0) {
console.log("Adding authorization from user side") console.log("Adding authorization from user side")
state += `%26authorization%3d${userAuth}`; state += `%26authorization%3d${userAuth}`;
} }
// Check for org_id // Check for org_id
const orgId = urlParams.get("org_id"); const orgId = urlParams.get("org_id");
if (orgId !== undefined && orgId !== null && orgId.length > 0) { if (orgId !== undefined && orgId !== null && orgId.length > 0) {
console.log("Adding org_id from user side") console.log("Adding org_id from user side")
state += `%26org_id%3d${orgId}`; state += `%26org_id%3d${orgId}`;
} }
if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) { if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) {
state += `%26oauth_url%3d${oauth_url}`; state += `%26oauth_url%3d${oauth_url}`;
@@ -363,7 +422,7 @@ const AuthenticationOauth2 = (props) => {
// No prompt forcing // No prompt forcing
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`; //var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
var defaultPrompt = "login" var defaultPrompt = "login"
if (prompt !== undefined && prompt !== null && prompt.length > 0) { if (prompt !== undefined && prompt !== null && prompt.length > 0) {
defaultPrompt = prompt defaultPrompt = prompt
} }
@@ -798,6 +857,7 @@ const AuthenticationOauth2 = (props) => {
}} }}
fullWidth fullWidth
color="primary" color="primary"
label={"Client ID"}
placeholder={"Client ID"} placeholder={"Client ID"}
onChange={(event) => { onChange={(event) => {
setClientId(event.target.value); setClientId(event.target.value);
@@ -816,20 +876,22 @@ const AuthenticationOauth2 = (props) => {
}} }}
fullWidth fullWidth
color="primary" color="primary"
label={"Client Secret"}
placeholder={"Client Secret"} placeholder={"Client Secret"}
onChange={(event) => { onChange={(event) => {
setClientSecret(event.target.value); setClientSecret(event.target.value);
//authenticationOption.label = event.target.value //authenticationOption.label = event.target.value
}} }}
/> />
{allscopes.length === 0 ? null : "Scopes"}
{allscopes.length === 0 ? null : ( {allscopes.length === 0 ? null : (
<div style={{width: "100%", marginTop: 10, display: "flex"}}> <div style={{width: "100%", marginTop: 10, display: "flex"}}>
<span> <span>
Scopes
<Select <Select
multiple multiple
underline={false} underline={false}
value={selectedScopes} value={selectedScopes}
label="Scopes"
style={{ style={{
backgroundColor: theme.palette.inputColor, backgroundColor: theme.palette.inputColor,
color: "white", color: "white",
@@ -855,17 +917,20 @@ const AuthenticationOauth2 = (props) => {
})} })}
</Select> </Select>
</span> </span>
<span>
<Tooltip {((authenticationType.redirect_uri === undefined || authenticationType.redirect_uri === null || authenticationType.redirect_uri.length === 0) && (authenticationType.token_uri !== undefined && authenticationType.token_uri !== null && authenticationType.token_uri.length > 0)) ? null :
color="primary" <span>
title={"Automatic Refresh (default: true)"} <Tooltip
placement="top" color="primary"
> title={"Automatic Refresh (default: true)"}
<Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => { placement="top"
setOfflineAccess(!offlineAccess) >
}}/> <Checkbox style={{paddingTop: 20}} color="secondary" checked={offlineAccess} onClick={() => {
</Tooltip> setOfflineAccess(!offlineAccess)
</span> }}/>
</Tooltip>
</span>
}
</div> </div>
)} )}
</span> </span>
@@ -882,19 +947,14 @@ const AuthenticationOauth2 = (props) => {
variant="contained" variant="contained"
fullWidth fullWidth
onClick={() => { onClick={() => {
handleOauth2Request( handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes);
clientId,
clientSecret,
oauthUrl,
selectedScopes
);
}} }}
color="primary" color="primary"
> >
{buttonClicked ? ( {buttonClicked ? (
<CircularProgress style={{ color: "white" }} /> <CircularProgress style={{ color: "white" }} />
) : ( ) : (
"Manually Authenticate" "Authenticate"
)} )}
</Button> </Button>
+1 -1
View File
@@ -81,7 +81,7 @@ const SearchField = props => {
style: { style: {
color: "white", color: "white",
minWidth: 750, minWidth: 750,
height: 685, height: 785,
borderRadius: 16, borderRadius: 16,
border: "1px solid var(--Container-Stroke, #494949)", border: "1px solid var(--Container-Stroke, #494949)",
background: "var(--Container, #000000)", background: "var(--Container, #000000)",
-1
View File
@@ -4593,7 +4593,6 @@ const AngularWorkflow = (defaultprops) => {
} }
} }
console.log("NODE: ", nodedata)
if (nodedata.decorator !== true && nodedata.attachedTo === undefined) { if (nodedata.decorator !== true && nodedata.attachedTo === undefined) {
var newdata = JSON.parse(JSON.stringify(nodedata)) var newdata = JSON.parse(JSON.stringify(nodedata))
newdata.large_image = "" newdata.large_image = ""
+216 -134
View File
@@ -374,8 +374,8 @@ const AppCreator = (defaultprops) => {
"API key", "API key",
"Bearer auth", "Bearer auth",
"Basic auth", "Basic auth",
"Oauth2",
"JWT", "JWT",
"Oauth2",
]; ];
const apikeySelection = ["Header", "Query"]; const apikeySelection = ["Header", "Query"];
@@ -411,6 +411,7 @@ const AppCreator = (defaultprops) => {
const [appBuilding, setAppBuilding] = useState(false); const [appBuilding, setAppBuilding] = useState(false);
const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false); const [fileDownloadEnabled, setFileDownloadEnabled] = useState(false);
const [actionAmount, setActionAmount] = useState(increaseAmount); const [actionAmount, setActionAmount] = useState(increaseAmount);
const [oauth2Type, setOauth2Type] = useState("application");
const defaultAuth = { const defaultAuth = {
name: "", name: "",
type: "header", type: "header",
@@ -1678,7 +1679,7 @@ const AppCreator = (defaultprops) => {
value.in.length > 0 value.in.length > 0
) { ) {
setParameterName(value.in); setParameterName(value.in);
optionset = true optionset = true
} }
} else if (value.scheme === "bearer") { } else if (value.scheme === "bearer") {
@@ -1709,24 +1710,24 @@ const AppCreator = (defaultprops) => {
newauth.push({ newauth.push({
"name": key, "name": key,
"type": value.in.toLowerCase(), "type": value.in.toLowerCase(),
"in": value.in.toLowerCase(), "in": value.in.toLowerCase(),
"example": "", "example": "",
}) })
} else { } else {
newauth.push({ newauth.push({
"name": key, "name": key,
"type": value.in.toLowerCase(), "type": value.in.toLowerCase(),
"in": value.in.toLowerCase(), "in": value.in.toLowerCase(),
"example": "", "example": "",
}) })
} }
if (value.description !== undefined && value.description !== null && value.description.length > 0) { if (value.description !== undefined && value.description !== null && value.description.length > 0) {
// Don't want a real description - just the ones we're replacing with // Don't want a real description - just the ones we're replacing with
if ((value.description.split(" ").length - 1) <= 2) { if ((value.description.split(" ").length - 1) <= 2) {
setRefreshUrl(value.description) setRefreshUrl(value.description)
} }
} }
} else if (value.scheme === "basic") { } else if (value.scheme === "basic") {
setAuthenticationOption("Basic auth"); setAuthenticationOption("Basic auth");
@@ -1734,15 +1735,15 @@ const AppCreator = (defaultprops) => {
optionset = true optionset = true
} else if (value.scheme === "oauth2") { } else if (value.scheme === "oauth2") {
setAuthenticationOption("Oauth2"); setAuthenticationOption("Oauth2");
setAuthenticationRequired(true); setAuthenticationRequired(true);
optionset = true optionset = true
} else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) { } else if (value.type === "oauth2" || key === "Oauth2" || key === "Oauth2c" || (key !== undefined && key !== null && key.toLowerCase().includes("oauth2"))) {
//toast("Can't handle Oauth2 auth yet.") //toast("Can't handle Oauth2 auth yet.")
setAuthenticationOption("Oauth2"); setAuthenticationOption("Oauth2");
setAuthenticationRequired(true); setAuthenticationRequired(true);
optionset = true optionset = true
//console.log("FLOW-1: ", value) //console.log("FLOW-1: ", value)
const flowkey = value.flow === undefined ? "flows" : "flow"; const flowkey = value.flow === undefined ? "flows" : "flow";
@@ -1754,12 +1755,11 @@ const AppCreator = (defaultprops) => {
//console.log("FLOW2: ", value[flowkey][basekey]) //console.log("FLOW2: ", value[flowkey][basekey])
if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined
) { ) {
if ( if (value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0) {
value[flowkey][basekey].authorizationUrl !== undefined && setParameterName(value[flowkey][basekey].authorizationUrl);
parameterName.length === 0 } else {
) { setOauth2Type("application")
setParameterName(value[flowkey][basekey].authorizationUrl); }
}
var tokenUrl = ""; var tokenUrl = "";
if (value[flowkey][basekey].tokenUrl !== undefined) { if (value[flowkey][basekey].tokenUrl !== undefined) {
@@ -2846,7 +2846,6 @@ const AppCreator = (defaultprops) => {
style={{ margin: 0, flex: "1", backgroundColor: inputColor }} style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
fullWidth={true} fullWidth={true}
placeholder="/security/user/authenticate" placeholder="/security/user/authenticate"
type="name"
id="standard-required" id="standard-required"
margin="normal" margin="normal"
variant="outlined" variant="outlined"
@@ -2866,6 +2865,40 @@ const AppCreator = (defaultprops) => {
}, },
}} }}
/> />
<Typography
variant="body2"
color="textSecondary"
style={{ marginTop: 10 }}
>
Optional: Authentication queries
</Typography>
{/*
<TextField
style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
fullWidth={true}
placeholder="grant_type=client_credentials&scope=connect.api.read"
id=""
margin="normal"
variant="outlined"
defaultValue={parameterName}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
Must use 'key=value&key=value' format
</span>
}
onBlur={(e) => {
//setParameterName(e.target.value)
}}
InputProps={{
classes: {
notchedOutline: classes.notchedOutline,
},
style: {
color: "white",
},
}}
/>
*/}
</div> </div>
) : null; ) : null;
@@ -2878,56 +2911,64 @@ const AppCreator = (defaultprops) => {
color="textSecondary" color="textSecondary"
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
> >
Find the Authorization URL, Token URL and scopes in question for the API. Ensure the app in question is pointed at https://shuffler.io/set_authentication {oauth2Type === "delegated" ?
"Find the Authorization URL, Token URL and scopes in question for the API. Ensure your app in the service uses redirect url https://shuffler.io/set_authentication"
:
"Find the Token URL and scopes in question for the API"
}
</Typography> </Typography>
<Typography {oauth2Type === "delegated" ?
variant="body2" <span>
color="textSecondary" <Typography
style={{ marginTop: 10 }} variant="body2"
> color="textSecondary"
Base Authorization URL for Oauth2 style={{ marginTop: 10 }}
</Typography> >
<TextField Base Authorization URL for Oauth2
required </Typography>
style={{ margin: 0, flex: "1", backgroundColor: inputColor }} <TextField
fullWidth={true} required
placeholder="https://.../oauth2/authorize" style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
type="name" fullWidth={true}
id="standard-required" placeholder="https://.../oauth2/authorize"
margin="normal" type="name"
variant="outlined" id="standard-required"
value={parameterName} margin="normal"
onChange={(e) => setParameterName(e.target.value)} variant="outlined"
value={parameterName}
onChange={(e) => setParameterName(e.target.value)}
onBlur={(event) => { onBlur={(event) => {
var tmpstring = event.target.value.trim(); var tmpstring = event.target.value.trim();
if ( if (
tmpstring.length > 4 && tmpstring.length > 4 &&
!tmpstring.startsWith("http") && !tmpstring.startsWith("http") &&
!tmpstring.startsWith("ftp") !tmpstring.startsWith("ftp")
) { ) {
toast("Auth URL must start with http(s)://"); toast("Auth URL must start with http(s)://");
} }
if (tmpstring.includes("?")) { if (tmpstring.includes("?")) {
var newtmp = tmpstring.split("?") var newtmp = tmpstring.split("?")
if (tmpstring.length > 1) { if (tmpstring.length > 1) {
tmpstring = newtmp[0] tmpstring = newtmp[0]
} }
} }
setParameterName(tmpstring) setParameterName(tmpstring)
}} }}
InputProps={{ InputProps={{
classes: { classes: {
notchedOutline: classes.notchedOutline, notchedOutline: classes.notchedOutline,
}, },
style: { style: {
color: "white", color: "white",
}, },
}} }}
/> />
</span>
: null}
<Typography <Typography
variant="body2" variant="body2"
color="textSecondary" color="textSecondary"
@@ -2977,75 +3018,79 @@ const AppCreator = (defaultprops) => {
}, },
}} }}
/> />
<Typography {oauth2Type === "delegated" ?
variant="body2" <span>
color="textSecondary" <Typography
style={{ marginTop: 10 }} variant="body2"
> color="textSecondary"
Refresh-token URL for Oauth2 (Optional) style={{ marginTop: 10 }}
</Typography> >
<TextField Refresh-token URL for Oauth2 (Optional)
style={{ margin: 0, flex: "1", backgroundColor: inputColor }} </Typography>
fullWidth={true} <TextField
placeholder="The URL to retrieve refresh-tokens at" style={{ margin: 0, flex: "1", backgroundColor: inputColor }}
type="name" fullWidth={true}
id="standard-required" placeholder="The URL to retrieve refresh-tokens at"
margin="normal" type="name"
variant="outlined" id="standard-required"
value={refreshUrl} margin="normal"
onChange={(e) => setRefreshUrl(e.target.value)} variant="outlined"
onBlur={(event) => { value={refreshUrl}
var tmpstring = event.target.value.trim(); onChange={(e) => setRefreshUrl(e.target.value)}
onBlur={(event) => {
var tmpstring = event.target.value.trim();
if ( if (
tmpstring.length > 4 && tmpstring.length > 4 &&
!tmpstring.startsWith("http") && !tmpstring.startsWith("http") &&
!tmpstring.startsWith("ftp") !tmpstring.startsWith("ftp")
) { ) {
toast("Refresh URL must start with http(s)://"); toast("Refresh URL must start with http(s)://");
} }
if (tmpstring.includes("?")) { if (tmpstring.includes("?")) {
var newtmp = tmpstring.split("?") var newtmp = tmpstring.split("?")
if (tmpstring.length > 1) { if (tmpstring.length > 1) {
tmpstring = newtmp[0] tmpstring = newtmp[0]
} }
} }
setRefreshUrl(tmpstring) setRefreshUrl(tmpstring)
}} }}
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
}, },
}} }}
/> />
<Typography </span>
variant="body2" : null}
color="textSecondary" <Typography
style={{ marginTop: 10 }} variant="body2"
> color="textSecondary"
Scopes for Oauth2 style={{ marginTop: 10 }}
</Typography> >
<MuiChipsInput Scopes for Oauth2
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}} </Typography>
<MuiChipsInput
style={{border: "2px solid #f86a3e", borderRadius: theme.palette.borderRadius,}}
required required
InputProps={{ InputProps={{
style: { style: {
color: "white", color: "white",
maxHeight: 50, maxHeight: 50,
}, },
}} }}
style={{ maxHeight: 80, overflowX: "hidden", overflowY: "auto" }} style={{ maxHeight: 80, overflowX: "hidden", overflowY: "auto" }}
placeholder="Available Oauth2 Scopes" placeholder="Available Oauth2 Scopes (enter to add)"
color="primary" color="primary"
fullWidth fullWidth
value={oauth2Scopes} value={oauth2Scopes}
onChange={(chips) => { onChange={(chips) => {
setOauth2Scopes(chips) setOauth2Scopes(chips)
setUpdate(Math.random()) setUpdate(Math.random())
}} }}
/> />
</div> </div>
) : null; ) : null;
@@ -5865,6 +5910,7 @@ const AppCreator = (defaultprops) => {
}} }}
/> />
<div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}> <div style={{padding: 25, border: "2px solid rgba(255,255,255,0.7)", borderRadius: theme.palette.borderRadius, }}>
<span style={{display: "flex", }}>
<FormControl style={{ }} variant="outlined"> <FormControl style={{ }} variant="outlined">
<Typography variant="h6">Authentication</Typography> <Typography variant="h6">Authentication</Typography>
<a <a
@@ -5889,7 +5935,7 @@ const AppCreator = (defaultprops) => {
setParameterLocation("") setParameterLocation("")
} }
setExtraAuth([]) setExtraAuth([])
} }
}} }}
value={authenticationOption} value={authenticationOption}
@@ -5910,6 +5956,42 @@ const AppCreator = (defaultprops) => {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
{authenticationOption === "Oauth2" ?
<FormControl style={{ marginLeft: 350, maxWidth: 200, }} variant="outlined">
{/*
<Typography variant="body2">
- Delegated: The user will get a popup for access their personal data.
- Application: Permissions are set by the app creator in the 3rd party platform.
</Typography>
*/}
<Select
fullWidth
label="Oauth2 type"
onChange={(e) => {
setOauth2Type(e.target.value);
}}
value={oauth2Type}
style={{
backgroundColor: inputColor,
color: "white",
height: "50px",
}}
>
{["delegated", "application"].map((data, index) => (
<MenuItem
key={index}
style={{ backgroundColor: inputColor, color: "white" }}
value={data}
>
{data}
</MenuItem>
))}
</Select>
</FormControl>
: null}
</span>
<div style={{marginTop: 15 }} /> <div style={{marginTop: 15 }} />
{basicAuth} {basicAuth}
{bearerAuth} {bearerAuth}