forked from confused-Techie/atom-backend
-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate last handlers, support RAW endpoint types for increased contr…
…ol of HTTP handling
- Loading branch information
1 parent
97bdd56
commit 39d961a
Showing
7 changed files
with
324 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
module.exports = { | ||
docs: { | ||
|
||
}, | ||
endpoint: { | ||
method: "GET", | ||
paths: [ "/api/login" ], | ||
rateLimit: "auth", | ||
successStatus: 200, | ||
options: { | ||
Allow: "GET", | ||
"X-Content-Type-Options": "nosniff" | ||
}, | ||
endpointKind: "raw" | ||
}, | ||
|
||
async logic(req, res, context) { | ||
// The first point of contact to log into the app. | ||
// Since this will be the endpoint for a user to login, we need | ||
// to redirect to GH. | ||
// @see https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps | ||
|
||
// Generate a random key | ||
const stateKey = context.utils.generateRandomString(64); | ||
|
||
// Before redirect, save the key into the db | ||
const saveStateKey = await context.database.authStoreStateKey(stateKey); | ||
|
||
if (!saveStateKey.ok) { | ||
res.status(500).json({ | ||
message: "Application Error: Failed to generate secure state key." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
res.status(302).redirect( | ||
`https://github.com/login/oauth/authorize?client_id=${context.config.GH_CLIENTID}&redirect_uri=${context.config.GH_REDIRECTURI}&state=${stateKey}&scope=public_repo%20read:org` | ||
); | ||
|
||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
const superagent = require("superagent"); | ||
|
||
module.exports = { | ||
docs: { | ||
|
||
}, | ||
endpoint: { | ||
method: "GET", | ||
paths: [ "/api/oauth" ], | ||
rateLimit: "auth", | ||
successStatus: 200, | ||
options: { | ||
Allow: "GET", | ||
"X-Content-Type-Options": "nosniff" | ||
}, | ||
endpointKind: "raw" | ||
}, | ||
|
||
async logic(req, res, context) { | ||
let params = { | ||
state: req.query.state ?? "", | ||
code: req.query.code ?? "" | ||
}; | ||
|
||
// First we want to ensure that the received state key is valid | ||
const validStateKey = await context.database.authCheckAndDeleteStateKey(params.state); | ||
|
||
if (!validStateKey.ok) { | ||
res.status(500).json({ | ||
message: "Application Error: Invalid State Key provided." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// Retrieve access token | ||
const initialAuth = await superagent | ||
.post("https://github.com/login/oauth/access_token") | ||
.query({ | ||
code: params.code, | ||
redirect_uri: context.config.GH_REDIRECTURI, | ||
client_id: context.config.GH_CLIENTID, | ||
client_secret: context.config.GH_CLIENTSECRET | ||
}); | ||
|
||
const accessToken = iniitalAuth.body?.access_token; | ||
|
||
if (accessToken === null || initialAuth.body?.token_type === null) { | ||
res.status(500).json({ | ||
message: "Application Error: Authentication to GitHub failed." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
try { | ||
// Request the user data using the access token | ||
const userData = await superagent | ||
.get("https://api.github.com/user") | ||
.set({ Authorization: `Bearer ${accessToken}` }) | ||
.set({ "User-Agent": context.config.GH_USERAGENT }); | ||
|
||
if (userData.status !== 200) { | ||
res.status(500).json({ | ||
message: `Application Error: Received HTTP Status ${userData.status}` | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// Now retrieve the user data that we need to store into the DB | ||
const username = userData.body.login; | ||
const userId = userData.body.node_id; | ||
const userAvatar = userData.body.avatar_url; | ||
|
||
const userExists = await context.database.getUserByNodeID(userId); | ||
|
||
if (userExists.ok) { | ||
// This means that the user does in fact already exist. | ||
// And from there they are likely reauthenticating, | ||
// But since we don't save any type of auth tokens, the user just needs | ||
// a new one and we should return their new one to them. | ||
|
||
// Now we redirect to the frontend site | ||
res.redirect(`https://web.pulsar-edit.dev/users?token=${accessToken}`); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// The user does not exist, so we save its data into the DB | ||
let createdUser = await context.database.insertNewUser( | ||
username, | ||
userId, | ||
userAvatar | ||
); | ||
|
||
if (!createdUser.ok) { | ||
res.status(500).json({ | ||
message: "Application Error: Creating the user account failed!" | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// Before returning, lets append their access token | ||
createdUser.content.token = accessToken; | ||
|
||
// Now re redirect to the frontend site | ||
res.redirect( | ||
`https://web.pulsar-edit.dev/users?token=${createdUser.content.token}` | ||
); | ||
context.logger.httpLog(req, res); | ||
return; | ||
|
||
} catch(err) { | ||
context.logger.generic(2, "/api/oauth Caught an Error!", { type: "error", err: err }); | ||
res.status(500).json({ | ||
message: "Application Error: The server encountered an error processing the request." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
const superagent = require("superagent"); | ||
|
||
module.exports = { | ||
docs: { | ||
|
||
}, | ||
endpoint: { | ||
method: "GET", | ||
paths: [ "/api/pat" ], | ||
rateLimit: "auth", | ||
successStatus: 200, | ||
options: { | ||
Allow: "GET", | ||
"X-Content-Type-Options": "nosniff" | ||
}, | ||
endpointKind: "raw" | ||
}, | ||
|
||
async logic(req, res, context) { | ||
let params = { | ||
token: req.query.token ?? "" | ||
}; | ||
|
||
if (params.token === "") { | ||
res.status(404).json({ | ||
message: "Not Found: Parameter 'token' is empty." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
try { | ||
const userData = await superagent | ||
.get("https://api.github.com/user") | ||
.set({ Authorization: `Bearer ${params.token}` }) | ||
.set({ "User-Agent": context.config.GH_USERAGENT }); | ||
|
||
if (userData.status !== 200) { | ||
context.logger.generic(2, "User Data request to GitHub failed!", { | ||
type: "object", | ||
obj: userData | ||
}); | ||
res.status(500).json({ | ||
message: `Application Error: Received HTTP Status ${userData.status} when contacting GitHub!` | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// Now to build a valid user object | ||
const username = userData.body.login; | ||
const userId = userData.body.node_id; | ||
const userAvatar = userData.body.avatar_url; | ||
|
||
const userExists = await context.database.getUserByNodeID(userId); | ||
|
||
if (userExists.ok) { | ||
// If we plan to allow updating the user name or image, we would do so here | ||
|
||
// Now to redirect to the frontend site. | ||
res.redirect(`https://web.pulsar-edit.dev/users?token=${params.token}`); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
let createdUser = await context.database.insertNewUser( | ||
username, | ||
userId, | ||
userAvatar | ||
); | ||
|
||
if (!createdUser.ok) { | ||
context.logger.generic(2, `Creating user failed! ${username}`); | ||
res.status(500).json({ | ||
message: "Application Error: Creating the user account failed!" | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
|
||
// Before returning, lets append their PAT token | ||
createdUser.content.token = params.token; | ||
|
||
res.redirect( | ||
`https://web.pulsar-edit.dev/users?token=${createdUser.cotnent.token}` | ||
); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} catch(err) { | ||
context.logger.generic(2, "/api/pat Caught an Error!", { type: "error", err: err }); | ||
res.status(500).json({ | ||
message: "Application Error: The server encountered an error processing the request." | ||
}); | ||
context.logger.httpLog(req, res); | ||
return; | ||
} | ||
} | ||
}; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.