-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Persist Account keys on login #375
Changes from 10 commits
6471600
6669958
cc3d12f
0d3e436
fa99e44
d99af06
00d3b94
62006db
6a3a6d9
4a43f8c
3e8e58e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,18 +12,6 @@ const clientSecret = | |
const REDIRECT_URI = `http://127.0.0.1`; | ||
|
||
class OAuthClient { | ||
server; //: http.Server; | ||
|
||
port; //: number; | ||
|
||
code_verifier; //: string; | ||
|
||
code_challenge; //: string; | ||
|
||
auth_code; //: string; | ||
|
||
state; //: string; | ||
|
||
constructor() { | ||
this.server = http.createServer(this._handleRequest.bind(this)); | ||
this.code_verifier = Buffer.from(randomBytes(20)).toString("base64url"); | ||
|
@@ -127,11 +115,11 @@ class OAuthClient { | |
|
||
async start() { | ||
try { | ||
this.server.on("listening", () => { | ||
this.port = this.server.address().port; | ||
this.server.emit("ready"); | ||
}); | ||
if (!this.server.listening) { | ||
this.server.on("listening", () => { | ||
this.port = this.server.address().port; | ||
this.server.emit("ready"); | ||
}); | ||
this.server.listen(0); | ||
Comment on lines
+119
to
123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this check was a workaround for that builtYargs.argv weirdness, so not strictly necessary but yes by the time i send "ready" it's already |
||
} | ||
} catch (e) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,13 +2,35 @@ | |
|
||
import { container } from "../cli.mjs"; | ||
|
||
/** | ||
* Class representing a client for interacting with the Fauna account API. | ||
*/ | ||
export class FaunaAccountClient { | ||
/** | ||
* Creates an instance of FaunaAccountClient. | ||
*/ | ||
constructor() { | ||
/** | ||
* The base URL for the Fauna account API. | ||
* @type {string} | ||
*/ | ||
this.url = | ||
process.env.FAUNA_ACCOUNT_URL ?? "https://account.fauna.com/api/v1"; | ||
|
||
/** | ||
* The fetch function for making HTTP requests. | ||
* @type {Function} | ||
*/ | ||
this.fetch = container.resolve("fetch"); | ||
} | ||
|
||
/** | ||
* Starts an OAuth request to the Fauna account API. | ||
* | ||
* @param {Object} authCodeParams - The parameters for the OAuth authorization code request. | ||
* @returns {Promise<string>} - The URL to the Fauna dashboard for OAuth authorization. | ||
* @throws {Error} - Throws an error if there is an issue during login. | ||
*/ | ||
async startOAuthRequest(authCodeParams) { | ||
const OAuthUrl = `${this.url}/api/v1/oauth/authorize?${new URLSearchParams( | ||
authCodeParams | ||
|
@@ -21,6 +43,18 @@ export class FaunaAccountClient { | |
return dashboardOAuthURL; | ||
} | ||
|
||
/** | ||
* Retrieves an access token from the Fauna account API. | ||
* | ||
* @param {Object} opts - The options for the token request. | ||
* @param {string} opts.clientId - The client ID for the OAuth application. | ||
* @param {string} opts.clientSecret - The client secret for the OAuth application. | ||
* @param {string} opts.authCode - The authorization code received from the OAuth authorization. | ||
* @param {string} opts.redirectURI - The redirect URI for the OAuth application. | ||
* @param {string} opts.codeVerifier - The code verifier for the OAuth PKCE flow. | ||
mwilde345 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @returns {Promise<string>} - The access token. | ||
* @throws {Error} - Throws an error if there is an issue during token retrieval. | ||
*/ | ||
async getToken(opts) { | ||
const params = { | ||
grant_type: "authorization_code", | ||
|
@@ -43,20 +77,26 @@ export class FaunaAccountClient { | |
`Failure to authorize with Fauna (${response.status}): ${response.statusText}` | ||
); | ||
} | ||
const { /*state,*/ access_token } = await response.json(); | ||
const { access_token } = await response.json(); | ||
return access_token; | ||
} catch (err) { | ||
throw new Error("Failure to authorize with Fauna: " + err.message); | ||
} | ||
} | ||
|
||
// TODO: remove access_token param and use credential manager helper | ||
/** | ||
* Retrieves the session information from the Fauna account API. | ||
* | ||
* @param {string} accessToken - The access token for the session. | ||
* @returns {Promise<{account_key: string, refresh_token: string}>} - The session information. | ||
* @throws {Error} - Throws an error if there is an issue during session retrieval. | ||
*/ | ||
async getSession(accessToken) { | ||
const headers = new Headers(); | ||
headers.append("Authorization", `Bearer ${accessToken}`); | ||
|
||
const requestOptions = { | ||
method: "POST", | ||
method: "GET", | ||
headers, | ||
}; | ||
try { | ||
|
@@ -66,22 +106,26 @@ export class FaunaAccountClient { | |
); | ||
if (response.status >= 400) { | ||
throw new Error( | ||
`Error creating session (${response.status}): ${response.statusText}` | ||
`Failure to get session with Fauna (${response.status}): ${response.statusText}` | ||
); | ||
} | ||
const session = await response.json(); | ||
return session; | ||
return await response.json(); | ||
} catch (err) { | ||
throw new Error( | ||
"Failure to create session with Fauna: " + JSON.stringify(err) | ||
); | ||
throw new Error("Failure to get session with Fauna: " + err.message); | ||
} | ||
} | ||
|
||
// TODO: remove account_key param and use credential manager helper | ||
async listDatabases(account_key) { | ||
/** | ||
* Lists databases associated with the given account key. | ||
* | ||
* @param {string} accountKey - The account key to list databases for. | ||
* @returns {Promise<Object[]>} - The list of databases. | ||
* @throws {Error} - Throws an error if there is an issue during the request. | ||
*/ | ||
async listDatabases(accountKey) { | ||
const headers = new Headers(); | ||
headers.append("Authorization", `Bearer ${account_key}`); | ||
headers.append("Authorization", `Bearer ${accountKey}`); | ||
|
||
Comment on lines
+127
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thoughts on abstracting out something with an API like |
||
const requestOptions = { | ||
method: "GET", | ||
headers, | ||
|
@@ -93,13 +137,12 @@ export class FaunaAccountClient { | |
); | ||
if (response.status >= 400) { | ||
throw new Error( | ||
`Error listing databases (${response.status}): ${response.statusText}` | ||
`Failure to list databases. (${response.status}): ${response.statusText}` | ||
); | ||
} | ||
const databases = await response.json(); | ||
return databases; | ||
return await response.json(); | ||
} catch (err) { | ||
throw new Error("Failure to list databases: ", err.message); | ||
throw new Error("Failure to list databases with Fauna: " + err.message); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will jsdoc this in another pr, this one is getting big
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the JSDoc PR can you add the annotation
//@ts-check
to the top of the file? it'll turn on in-IDE static analysis and should help you generate good types.