#
OAuth Verifier
The OAuth Veriifer is an abstraction to make writing verifiers easier. It is built for verifiers where the verification is done by having the user authenticate with the platform via OAuth.
#
Constructor
type OAuthData = {
claimType: ClaimType
documentationImages?: DocumentationImage[]
getUrl: () => Promise<Result<string>>
getToken: (data: any) => Promise<Result<TokenResponse>>
isTokenValid: (challengeResponse: any, id: string) => Promise<Result>
}
type DocumentationImage = {
url: string
caption: string
}
#
claimType
This is currently a static enum of types in the Polycentric repo. In the future, Polycentric will support dynamic claim types.
#
documentationImages
This is an array of image links that will be displayed in the documentation for the verifier when using a verifier client such as Harbor. For example, documentationImages could include screenshots of how to edit a channel biography on YouTube. If no array is passed in, no images will be displayed.
#
getUrl
This function should return a URL which the user could be redirected to in order to sign into the platform. You can add a harborSecret URI parameter which the client will store and send with the getToken request. This function returns a promise that resolves to a string Result.
#
getToken
This function takes in an any that contains the data returned by the oauth callback inside of an object. It also optionally includes a harborSecret property which contains arbitrary data that ws included in the url returned by getUrl. This function returns a promise that resolves to a TokenResponse Result.
#
isTokenValid
This function takes a token and a username as paramaters. It then checks to see wether the token is associated with the username. This function returns a promise that resolves to a void Result.
#
Usage
This is an example of an implementation of the OAuth verifier to verify X/Twitter accounts:
export type TwitterToken = {
secret: string
token: string
}
type TwitterTokenRequest = {
oauth_token: string
oauth_verifier: string
harborSecret: string
}
const twitterClient = new TwitterApi({
appKey: process.env.TWITTER_API_KEY,
appSecret: process.env.TWITTER_API_SECRET,
})
const XOAuthVerifier = new OAuthVerifier({
claimType: ClaimType.Twitter,
getUrl: async () => {
let oauthRequest = await twitterClient.generateAuthLink(process.env.TWITTER_CALLBACK_URL)
return Result.ok(`${oauthRequest.url}&harborSecret=${oauthRequest.oauth_token_secret}`)
},
getToken: async (data: TwitterTokenRequest): Promise<Result<TokenResponse>> => {
try {
const client = new TwitterApi({
appKey: process.env.TWITTER_API_KEY,
appSecret: process.env.TWITTER_API_SECRET,
accessToken: data.oauth_token,
accessSecret: data.harborSecret,
})
const response = await client.login(data.oauth_verifier)
return Result.ok({
username: response.screenName,
token: encodeObject<TwitterToken>({
secret: response.accessSecret,
token: response.accessToken,
}),
})
} catch (err) {
if (err instanceof ApiResponseError) {
return httpResponseToError(err.code, JSON.stringify(err.data), 'X API Login')
}
throw err
}
},
isTokenValid: async (token: string, id: string) => {
const payload = decodeObject<TwitterToken>(token)
const client = new TwitterApi({
appKey: process.env.TWITTER_API_KEY,
appSecret: process.env.TWITTER_API_SECRET,
accessToken: payload.token,
accessSecret: payload.secret,
})
let response = await client.currentUser()
let res = response.screen_name
if (res !== id) {
return Result.err({
message: "The username didn't match the account you logged in with",
extendedMessage: `Username did not match (expected: ${id}, got: ${response.screen_name})`,
})
}
return Result.ok()
},
})