Created
November 2, 2023 07:54
-
-
Save melodyclue/e0c20eb7a4c86959cce0cb09863e1151 to your computer and use it in GitHub Desktop.
Chatwork OAuth
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
import { | |
OAuth2ProviderAuthWithPKCE, | |
createOAuth2AuthorizationUrlWithPKCE, | |
validateOAuth2AuthorizationCode | |
} from "../core/oauth2.js"; | |
import { ProviderUserAuth } from "../core/provider.js"; | |
import { handleRequest, authorizationHeader } from "../utils/request.js"; | |
import type { Auth } from "lucia"; | |
type Config = { | |
clientId: string; | |
clientSecret: string; | |
redirectUri: string; | |
scope?: string[]; | |
}; | |
const PROVIDER_ID = "chatwork"; | |
export const chatwork = <_Auth extends Auth = Auth>( | |
auth: _Auth, | |
config: Config | |
): ChatworkAuth<_Auth> => { | |
return new ChatworkAuth(auth, config); | |
}; | |
export class ChatworkAuth< | |
_Auth extends Auth = Auth | |
> extends OAuth2ProviderAuthWithPKCE<ChatworkUserAuth<_Auth>> { | |
private config: Config; | |
constructor(auth: _Auth, config: Config) { | |
super(auth); | |
this.config = config; | |
} | |
public getAuthorizationUrl = async (): Promise< | |
readonly [url: URL, codeVerifier: string, state: string] | |
> => { | |
const scopeConfig = this.config.scope ?? []; | |
return await createOAuth2AuthorizationUrlWithPKCE( | |
"https://www.chatwork.com/packages/oauth2/login.php", | |
{ | |
clientId: this.config.clientId, | |
codeChallengeMethod: "S256", | |
scope: ["users.all:read", "users.profile.me:read", ...scopeConfig], | |
redirectUri: this.config.redirectUri | |
} | |
); | |
}; | |
public validateCallback = async ( | |
code: string, | |
code_verifier: string | |
): Promise<ChatworkUserAuth<_Auth>> => { | |
const chatworkTokens = await this.validateAuthorizationCode( | |
code, | |
code_verifier | |
); | |
const chatworkUser = await getChatworkUser(chatworkTokens.accessToken); | |
return new ChatworkUserAuth(this.auth, chatworkUser, chatworkTokens); | |
}; | |
private validateAuthorizationCode = async ( | |
code: string, | |
codeVerifier: string | |
): Promise<ChatworkTokens> => { | |
const tokens = await validateOAuth2AuthorizationCode<{ | |
access_token: string; | |
refresh_token?: string; | |
}>(code, "https://oauth.chatwork.com/token", { | |
clientId: this.config.clientId, | |
redirectUri: this.config.redirectUri, | |
codeVerifier, | |
clientPassword: { | |
authenticateWith: "client_secret", | |
clientSecret: this.config.clientSecret | |
} | |
}); | |
return { | |
accessToken: tokens.access_token, | |
refreshToken: tokens.refresh_token ?? null | |
}; | |
}; | |
} | |
export class ChatworkUserAuth< | |
_Auth extends Auth = Auth | |
> extends ProviderUserAuth<_Auth> { | |
public chatworkTokens: ChatworkTokens; | |
public chatworkUser: ChatworkUser; | |
constructor( | |
auth: _Auth, | |
chatworkUser: ChatworkUser, | |
chatworkTokens: ChatworkTokens | |
) { | |
super(auth, PROVIDER_ID, chatworkUser.id); | |
this.chatworkTokens = chatworkTokens; | |
this.chatworkUser = chatworkUser; | |
} | |
} | |
const getChatworkUser = async (accessToken: string): Promise<ChatworkUser> => { | |
const request = new Request("https://api.chatwork.com/v2/me", { | |
headers: { | |
Authorization: authorizationHeader("bearer", accessToken) | |
} | |
}); | |
const chatworkUserResult = await handleRequest<{ | |
data: ChatworkUser; | |
}>(request); | |
return chatworkUserResult.data; | |
}; | |
export type ChatworkTokens = { | |
accessToken: string; | |
refreshToken: string | null; | |
}; | |
export type ChatworkUser = { | |
id: string; | |
name: string; | |
username: string; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment