forked from civicteam/passport
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'google_abstraction' of github.com:gitcoinco/passport in…
…to google_abstraction
- Loading branch information
Showing
10 changed files
with
587 additions
and
6 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
162 changes: 162 additions & 0 deletions
162
platforms/src/ClearText/Providers/clearTextGithubOrg.ts
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,162 @@ | ||
// ----- Types | ||
import type { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types"; | ||
import type { Provider, ProviderOptions } from "../../types"; | ||
import axios from "axios"; | ||
|
||
export type GithubTokenResponse = { | ||
access_token: string; | ||
}; | ||
|
||
export type GithubFindMyUserResponse = { | ||
id?: string; | ||
login?: string; | ||
type?: string; | ||
}; | ||
|
||
export enum ClientType { | ||
GrantHub, | ||
} | ||
|
||
export type GHUserRequestPayload = RequestPayload & { | ||
requestedClient: ClientType; | ||
org?: string; | ||
}; | ||
|
||
type GithubMyOrg = { | ||
providedOrg: string; | ||
matchingOrg: string; | ||
}; | ||
|
||
type GHVerification = { | ||
validOrg: GithubMyOrg; | ||
id: number; | ||
}; | ||
|
||
// Export a Github Provider to carry out OAuth and return a record object | ||
export class ClearTextGithubOrgProvider implements Provider { | ||
// Give the provider a type so that we can select it with a payload | ||
type = "ClearTextGithubOrg"; | ||
|
||
// Options can be set here and/or via the constructor | ||
_options = {}; | ||
|
||
// construct the provider instance with supplied options | ||
constructor(options: ProviderOptions = {}) { | ||
this._options = { ...this._options, ...options }; | ||
} | ||
|
||
// verify that the Github user is a memeber of the provided organization | ||
async verify(payload: GHUserRequestPayload): Promise<VerifiedPayload> { | ||
let valid = false, | ||
ghVerification: GHVerification, | ||
pii; | ||
console.log({ payload }); | ||
try { | ||
ghVerification = await verifyGithub(payload.proofs.code, payload.org, payload.requestedClient); | ||
} catch (e) { | ||
return { valid: false }; | ||
} finally { | ||
const validOrg = ghVerification?.validOrg; | ||
pii = validOrg ? `${validOrg.matchingOrg}#${ghVerification.id}` : ""; | ||
valid = validOrg && validOrg.matchingOrg === validOrg.providedOrg; | ||
} | ||
|
||
return { | ||
valid: valid, | ||
record: { | ||
pii, | ||
}, | ||
}; | ||
} | ||
} | ||
|
||
type GithubUserResponse = { | ||
status: number; | ||
data: { | ||
login: string; | ||
id: number; | ||
}; | ||
}; | ||
|
||
type Organization = { | ||
login: string; | ||
}; | ||
|
||
type GithubUserOrgResponse = { | ||
status: number; | ||
data: Organization[]; | ||
}; | ||
|
||
const verifyOrg = (data: Organization[], providedOrg: string): GithubMyOrg => { | ||
const orgs = data; | ||
const matchingOrgs = orgs.filter((org) => org.login === providedOrg); | ||
|
||
if (matchingOrgs.length !== 1) { | ||
throw `${providedOrg} not found in user profile`; | ||
} | ||
return { | ||
providedOrg, | ||
matchingOrg: matchingOrgs[0].login, | ||
}; | ||
}; | ||
|
||
const requestAccessToken = async (code: string, requestedClient: ClientType): Promise<string> => { | ||
const clientId = | ||
requestedClient === ClientType.GrantHub ? process.env.GRANT_HUB_GITHUB_CLIENT_ID : process.env.GITHUB_CLIENT_ID; | ||
const clientSecret = | ||
requestedClient === ClientType.GrantHub | ||
? process.env.GRANT_HUB_GITHUB_CLIENT_SECRET | ||
: process.env.GITHUB_CLIENT_SECRET; | ||
|
||
// Exchange the code for an access token | ||
const tokenRequest = await axios.post( | ||
`https://github.com/login/oauth/access_token?client_id=${clientId}&client_secret=${clientSecret}&code=${code}`, | ||
{}, | ||
{ | ||
headers: { Accept: "application/json" }, | ||
} | ||
); | ||
|
||
if (tokenRequest.status != 200) { | ||
throw `Post for request returned status code ${tokenRequest.status} instead of the expected 200`; | ||
} | ||
|
||
const tokenResponse = tokenRequest.data as GithubTokenResponse; | ||
|
||
return tokenResponse.access_token; | ||
}; | ||
|
||
const verifyGithub = async ( | ||
code: string, | ||
providedOrg: string, | ||
requestedClient: ClientType | ||
): Promise<GHVerification> => { | ||
// retrieve user's auth bearer token to authenticate client | ||
const accessToken = await requestAccessToken(code, requestedClient); | ||
|
||
// Now that we have an access token fetch the user details | ||
const userRequest: GithubUserResponse = await axios.get("https://api.github.com/user", { | ||
headers: { Authorization: `token ${accessToken}` }, | ||
}); | ||
if (userRequest.status != 200) { | ||
throw `Get user request returned status code ${userRequest.status} instead of the expected 200`; | ||
} | ||
|
||
const handle = userRequest.data.login; | ||
const { id } = userRequest.data; | ||
|
||
const userOrgRequest: GithubUserOrgResponse = await axios.get(`https://api.github.com/users/${handle}/orgs`, { | ||
headers: { Authorization: `token ${accessToken}` }, | ||
}); | ||
|
||
if (userOrgRequest.status != 200) { | ||
throw `Get user org request returned status code ${userOrgRequest.status} instead of the expected 200`; | ||
} | ||
|
||
const validOrg = verifyOrg(userOrgRequest.data, providedOrg); | ||
|
||
return { | ||
validOrg, | ||
id, | ||
}; | ||
}; |
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,58 @@ | ||
// ----- Types | ||
import type { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types"; | ||
|
||
// ----- Twitters OAuth2 library | ||
import { | ||
deleteClient, | ||
getClient, | ||
requestFindMyUser, | ||
TwitterFindMyUserResponse, | ||
} from "../../Twitter/procedures/twitterOauth"; | ||
import type { Provider, ProviderOptions } from "../../types"; | ||
// import { verifyTwitter } from "../providers/twitter"; | ||
|
||
// Export a Twitter Provider to carry out OAuth and return a record object | ||
export class ClearTextTwitterProvider implements Provider { | ||
// Give the provider a type so that we can select it with a payload | ||
type = "ClearTextTwitter"; | ||
// Options can be set here and/or via the constructor | ||
_options = {}; | ||
|
||
// construct the provider instance with supplied options | ||
constructor(options: ProviderOptions = {}) { | ||
this._options = { ...this._options, ...options }; | ||
} | ||
|
||
// verify that the proof object contains valid === "true" | ||
async verify(payload: RequestPayload): Promise<VerifiedPayload> { | ||
let valid = false, | ||
verifiedPayload: TwitterFindMyUserResponse = {}, | ||
pii; | ||
|
||
try { | ||
verifiedPayload = await verifyUserTwitter(payload.proofs.sessionKey, payload.proofs.code); | ||
} catch (e) { | ||
return { valid: false }; | ||
} finally { | ||
valid = verifiedPayload && verifiedPayload.username ? true : false; | ||
pii = verifiedPayload.username; | ||
} | ||
|
||
return { | ||
valid, | ||
record: { | ||
pii, | ||
}, | ||
}; | ||
} | ||
} | ||
|
||
async function verifyUserTwitter(sessionKey: string, code: string): Promise<TwitterFindMyUserResponse> { | ||
const client = getClient(sessionKey); | ||
|
||
const myUser = await requestFindMyUser(client, code); | ||
|
||
deleteClient(sessionKey); | ||
|
||
return myUser; | ||
} |
Oops, something went wrong.