-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroutes.js
More file actions
164 lines (138 loc) · 4.46 KB
/
Copy pathroutes.js
File metadata and controls
164 lines (138 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
const express = require("express");
const { createHmac } = require("crypto");
const { getTopicById, getTopicByKey } = require("../../pluginApi").topic;
const { validateDuoSignature } = require("../middleware/validate-duo");
const {
createPushToTopic,
pushToTopicDevices,
getPushByIdent,
getPushResponse,
} = require("../service/push");
const router = express.Router();
// The /ping endpoint acts as a "liveness check" that can be called to verify that Duo is up before trying to call other Auth API endpoints.
router.get("/ping", (request, response) => {
return response.json({
stat: "OK",
response: {
time: Math.round(Date.now() / 1000),
validation: process.env.NO_DUO_AUTH ? "skipped" : "enabled", // added to indicate if we're currently validating cert
},
});
});
// The /preauth endpoint determines whether a user is authorized to log in, and (if so) returns the user's available authentication factors.
router.post("/preauth", validateDuoSignature, async (request, response) => {
console.log("preauth", request.body, request.topic);
const { username } = request.body;
// no devices in topic, deny
if (!request?.topic?.devices || request.topic.devices.length === 0) {
return response.json({
stat: "OK",
response: {
devices: [],
result: "deny",
status_msg: "No devices in topic",
},
});
}
// loop devices, create array
const devices = request.topic.devices.map((device) => {
return {
capabilities: ["auto", "push"],
device: `${device.deviceKey}`,
display_name: `${device.name}`,
name: `${device.name}`,
number: "",
type: "phone",
};
});
return response.json({
stat: "OK",
response: {
devices: devices,
result: "auth", // TODO this should take into account lockouts, etc.
status_msg: "Account is active",
},
});
});
const WAIT_TIME = 30; // seconds
// The /auth endpoint performs second-factor authentication for a user by sending a push notification to the user's smartphone app, verifying a passcode, or placing a phone call.
router.post("/auth", validateDuoSignature, async (request, response) => {
console.log("auth", request.body);
const { device, username, factor, ipaddr, async } = request.body;
// non-async
if (!async) {
const pushPayload = {
categoryId: "button.approve_deny",
title: `Login Approval Request`,
subtitle: `Request from ${ipaddr}`,
body: `Approve login request for ${username}?`,
priority: "high",
ttl: WAIT_TIME,
};
const createdPush = await createPushToTopic(request.topic, pushPayload);
console.log("createdPush", pushPayload, createdPush);
await pushToTopicDevices(request.topic, createdPush, pushPayload);
// **wait** for push response here
const waitForResponse = async (pushId) => {
var totalLoops = 0; // set your counter to 1
function timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function checkForResponse() {
const response = await getPushResponse(pushId);
if (response) {
console.log("checkForResponse", response);
return response;
}
await timeout(1000);
totalLoops++; // increment the counter (seconds)
if (totalLoops < WAIT_TIME) {
return await checkForResponse();
} else {
return false;
}
}
return checkForResponse();
};
const result = await waitForResponse(createdPush.dataValues.id);
console.log("result", result);
if (JSON.parse(result.serviceResponse).actionIdentifier === "approve") {
return response.json({
stat: "OK",
response: {
result: "allow",
status: "allow",
},
serviceData: JSON.parse(result.serviceResponse),
});
} else {
return response.json({
stat: "OK",
response: {
result: "deny",
status: "deny",
},
serviceData: JSON.parse(result.serviceResponse),
});
}
} else {
return response.json({
stat: "OK",
response: {
txid: "45f7c92b-f45f-4862-8545-e0f58e78075a",
},
});
}
});
// unused for async
router.get("/auth_status", validateDuoSignature, (request, response) => {
console.log("auth_status", request.query);
return response.json({
stat: "OK",
response: {
result: "allow",
status: "allow",
},
});
});
module.exports = router;