x-api-sdk-ts
Version:
TypeScript Library for the X (ex-twitter) API V2
152 lines • 5.79 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuth2Auth = void 0;
const IOAuth2Auth_1 = require("../interfaces/auth/IOAuth2Auth");
const crypto_1 = __importDefault(require("crypto"));
class OAuth2Auth extends IOAuth2Auth_1.AbstractOAuth2Auth {
constructor(config, httpAdapter) {
super(config, httpAdapter);
if (!config.clientId) {
throw new Error('OAuth2Auth requires a client ID');
}
else if (!this.httpAdapter) {
throw new Error('OAuth2Auth requires a httpAdapter');
}
this.clientId = config.clientId;
this.clientSecret = config.clientSecret;
this.redirectUri = config.redirectUri;
this.state = config.state || this.generateState();
this.codeVerifier = config.codeVerifier || this.generateCodeVerifier();
this.scopes = config.scopes;
this.accessToken = config.accessToken || null;
this.refreshToken = config.refreshToken || null;
this.tokenExpiresAt = config.tokenExpiresAt || null;
}
setToken(token) {
this.accessToken = token.accessToken;
this.refreshToken = token.refreshToken;
this.tokenExpiresAt = token.tokenExpiresAt;
return this;
}
getToken() {
return {
accessToken: this.accessToken,
refreshToken: this.refreshToken,
tokenExpiresAt: this.tokenExpiresAt,
};
}
generateAuthorizeUrl(codeChallenge, codeChallengeMethod = 'S256') {
if (!codeChallenge) {
codeChallengeMethod = 'S256';
codeChallenge = this.generateCodeChallenge();
}
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
scope: this.scopes.join(' '),
state: this.state,
code_challenge: codeChallenge,
code_challenge_method: codeChallengeMethod,
});
if (this.redirectUri) {
params.set('redirect_uri', this.redirectUri);
}
return `https://x.com/i/oauth2/authorize?${params.toString()}`;
}
async exchangeAuthCodeForToken(code) {
const authHeader = 'Basic ' + Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
const params = new URLSearchParams({
grant_type: 'authorization_code',
code,
code_verifier: this.codeVerifier,
});
if (this.redirectUri) {
params.set('redirect_uri', this.redirectUri);
}
const response = await this.httpAdapter.fetch('https://api.x.com/2/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authHeader,
},
body: params.toString(),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to exchange authorization code: ${error}`);
}
const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token || null;
this.tokenExpiresAt = Date.now() + data.expires_in * 1000;
return this;
}
async getHeaders() {
if (!this.accessToken) {
throw new Error('No access token available');
}
if (this.isTokenExpired() && this.refreshToken) {
await this.refreshAccessToken();
}
if (!this.accessToken) {
throw new Error('Failed to obtain a valid access token');
}
return {
Authorization: `Bearer ${this.accessToken}`,
};
}
async refreshAccessToken() {
if (!this.refreshToken) {
throw new Error('No refresh token available to refresh access token');
}
const authHeader = 'Basic ' + Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
const response = await this.httpAdapter.fetch('https://api.x.com/2/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': authHeader,
},
body: new URLSearchParams({
refresh_token: this.refreshToken,
grant_type: 'refresh_token',
client_id: this.clientId,
}),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to refresh token: ${error}`);
}
const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token || this.refreshToken;
this.tokenExpiresAt = Date.now() + data.expires_in * 1000;
return this;
}
isTokenExpired() {
if (!this.tokenExpiresAt) {
return true;
}
return Date.now() >= this.tokenExpiresAt;
}
base64urlEncode(buffer) {
return buffer
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
generateCodeVerifier() {
return this.base64urlEncode(crypto_1.default.randomBytes(32));
}
generateCodeChallenge() {
return this.base64urlEncode(crypto_1.default.createHash('sha256').update(this.codeVerifier).digest());
}
generateState() {
return crypto_1.default.randomBytes(16).toString('hex');
}
}
exports.OAuth2Auth = OAuth2Auth;
//# sourceMappingURL=OAuth2Auth.js.map