In nodejs, I wrote code like below.
Instead of exclude credentials.json you need to offer clientId, clientSecret, refreshToken, redirectUrl as other way (ex. environment)
export class GmailService {
TOKEN_PATH: string = 'token.json';
SCOPES: string[] = ['https://www.googleapis.com/auth/gmail.send'];
refreshTokenUrl: string = 'https://oauth2.googleapis.com/token';
ACCESS_TYPE: string = 'offline';
oAuth2Client: any;
gmailClient: gmail_v1.Gmail;
constructor(private readonly configService: ConfigService) {
this.oAuth2Client = new google.auth.OAuth2(
this.configService.get(env.mailer.clientId),
this.configService.get(env.mailer.clientSecret),
this.configService.get(env.mailer.redirectUrl),
);
this.gmailClient = gmail({ version: 'v1', auth: this.oAuth2Client });
this.authorize().catch((err: Error) => {
throw err;
});
}
private static encodeMessage(msg: Buffer): string {
return Buffer.from(msg)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+&/g, '');
}
async sendMail(mail: Mail.Options): Promise<void> {
if (this.configService.get(env.environment) === 'test') return;
if (this.configService.get(env.environment) !== 'production') {
mail.to = this.configService.get(env.mailer.testTarget);
}
await this.authorize();
await this.send(mail);
}
async authorize(): Promise<void> {
// check token file exists
await fs.readFile(this.TOKEN_PATH, async (err: Error, tokenFile: any) => {
let token: Token;
if (err) {
token = await this.getNewToken(); // token file not exist
} else {
token = JSON.parse(tokenFile);
if (token.expiry_date - new Date().getTime() < 30000) {
token = await this.getNewToken(); // token was expired
}
}
this.oAuth2Client.setCredentials(token);
});
}
// refresh token
async getNewToken(): Promise<Token> {
const response: AxiosResponse = await axios.post(this.refreshTokenUrl, {
client_id: this.configService.get(env.mailer.clientId),
client_secret: this.configService.get(env.mailer.clientSecret),
grant_type: 'refresh_token',
refresh_token: this.configService.get(env.mailer.refreshToken),
});
const token: Token = response.data;
if (token.expires_in && !token.expiry_date) {
token.expiry_date = new Date().getTime() + token.expires_in * 1000;
}
await fs.writeFile(this.TOKEN_PATH, JSON.stringify(token), (err: Error) => {
if (err) throw err;
});
return token;
}
private async send(mail: Mail.Options): Promise<void> {
const mailComposer: MailComposer = new MailComposer(mail); // build mail with nodemailer
mailComposer.compile().build((err: Error, msg: Buffer) => {
if (err) throw err;
this.gmailClient.users.messages.send(
{
userId: 'me',
requestBody: {
raw: GmailService.encodeMessage(msg),
},
},
(err: Error, result: any) => {
if (err) throw err;
console.log('NODEMAILER reply from server', result.data);
},
);
});
}
}