Skip to content

Commit

Permalink
feat: emails can be sent with different locales (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheSlimvReal authored Nov 13, 2023
1 parent 3883dc6 commit 1135d85
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/account/account.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('AccountController', () => {
expect(mockHttp.put).toHaveBeenCalledWith(
expect.stringMatching(/\/user-id\/execute-actions-email/),
['VERIFY_EMAIL'],
expect.anything(),
);
// set roles
expect(mockHttp.post).toHaveBeenCalledWith(
Expand Down Expand Up @@ -107,6 +108,7 @@ describe('AccountController', () => {
`execute-actions-email?client_id=${user.client}`,
),
['VERIFY_EMAIL'],
expect.anything(),
);
// old roles are deleted
expect(mockHttp.delete).toHaveBeenCalledWith(
Expand Down Expand Up @@ -137,6 +139,7 @@ describe('AccountController', () => {
`execute-actions-email?client_id=${user.client}`,
),
['VERIFY_EMAIL'],
expect.anything(),
],
]);
done();
Expand All @@ -161,6 +164,7 @@ describe('AccountController', () => {
`${user.realm}/users/${user.sub}/execute-actions-email?client_id=${user.client}&redirect_uri=`,
),
['UPDATE_PASSWORD'],
expect.anything(),
);
done();
});
Expand Down
38 changes: 31 additions & 7 deletions src/account/account.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import {
Body,
Controller,
Get,
Headers,
Param,
Post,
Put,
Req,
UseGuards,
} from '@nestjs/common';
import { BearerGuard } from '../auth/bearer/bearer.guard';
import { ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { ApiBearerAuth, ApiHeader, ApiOperation } from '@nestjs/swagger';
import {
concatMap,
concatWith,
Expand Down Expand Up @@ -51,8 +52,13 @@ export class AccountController {
})
@UseGuards(BearerGuard)
@ApiBearerAuth()
@ApiHeader({ name: 'Accept-Language', required: false })
@Put('set-email')
setEmail(@Req() req, @Body() { email }: SetEmailReq) {
setEmail(
@Req() req,
@Body() { email }: SetEmailReq,
@Headers('Accept-Language') lang?: string,
) {
const user = req.user as User;
// TODO email is directly marked as verified
return this.keycloak
Expand All @@ -68,6 +74,7 @@ export class AccountController {
user.client,
user.sub,
'VERIFY_EMAIL',
lang,
),
),
prepareResult(),
Expand All @@ -79,12 +86,22 @@ export class AccountController {
description:
'Looks for the user with the given email and sends a reset password email',
})
@ApiHeader({ name: 'Accept-Language', required: false })
@Post('forgot-password')
forgotPassword(@Body() { email, realm, client }: ForgotEmailReq) {
forgotPassword(
@Body() { email, realm, client }: ForgotEmailReq,
@Headers('Accept-Language') lang?: string,
) {
return this.keycloak.findUserBy(realm, { email }).pipe(
// TODO only verified/valid accounts should allow a password reset?
concatMap((user) =>
this.keycloak.sendEmail(realm, client, user.id, 'UPDATE_PASSWORD'),
this.keycloak.sendEmail(
realm,
client,
user.id,
'UPDATE_PASSWORD',
lang,
),
),
prepareResult(),
);
Expand All @@ -110,18 +127,23 @@ export class AccountController {
`,
})
@ApiBearerAuth()
@ApiHeader({ name: 'Accept-Language', required: false })
@UseGuards(BearerGuard, RolesGuard)
@Roles(AccountController.ACCOUNT_MANAGEMENT_ROLE)
@Post()
createAccount(@Req() req, @Body() { username, email, roles }: NewAccount) {
createAccount(
@Req() req,
@Body() { username, email, roles }: NewAccount,
@Headers('Accept-Language') lang?: string,
) {
const user = req.user as User;
const { realm, client } = user;
let userId: string;
return this.keycloak.createUser(realm, username, email).pipe(
concatMap(() => this.keycloak.findUserBy(realm, { username })),
tap((res) => (userId = res.id)),
concatMap(() =>
this.keycloak.sendEmail(realm, client, userId, 'VERIFY_EMAIL'),
this.keycloak.sendEmail(realm, client, userId, 'VERIFY_EMAIL', lang),
),
concatMap(() => this.keycloak.assignRoles(user.realm, userId, roles)),
prepareResult(),
Expand Down Expand Up @@ -156,13 +178,15 @@ export class AccountController {
description: 'Partially update properties of a user.',
})
@ApiBearerAuth()
@ApiHeader({ name: 'Accept-Language', required: false })
@UseGuards(BearerGuard, RolesGuard)
@Roles(AccountController.ACCOUNT_MANAGEMENT_ROLE)
@Put('/:userId')
updateAccount(
@Req() req,
@Param('userId') userId: string,
@Body() updatedUser: KeycloakUser,
@Headers('Accept-Language') lang?: string,
) {
const { realm, client } = req.user as User;
const observables: Observable<any>[] = [];
Expand All @@ -181,7 +205,7 @@ export class AccountController {
// send verification email if email changed
updatedUser.requiredActions = ['VERIFY_EMAIL'];
observables.push(
this.keycloak.sendEmail(realm, client, userId, 'VERIFY_EMAIL'),
this.keycloak.sendEmail(realm, client, userId, 'VERIFY_EMAIL', lang),
);
}
// first update the user object, then run other observables
Expand Down
10 changes: 9 additions & 1 deletion src/account/keycloak.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,20 @@ export class KeycloakService {
* @param client
* @param userId
* @param action e.g. "UPDATE_PASSWORD", "VERIFY_EMAIL"
* @param lang
*/
sendEmail(realm: string, client: string, userId: string, action: string) {
sendEmail(
realm: string,
client: string,
userId: string,
action: string,
lang?: string,
) {
return this.perform(
this.http.put,
`${realm}/users/${userId}/execute-actions-email?client_id=${client}&redirect_uri=`,
[action],
{ headers: { 'Accept-Language': lang } },
);
}

Expand Down

0 comments on commit 1135d85

Please sign in to comment.