Passwords
Changing a password, the forgotten-password flow, and warning a user before theirs expires.
Only relevant where the server holds the passwords — a credentials deployment. With SSO, OAuth, SAML or a
token, the identity provider owns them and these calls do not apply.
Changing a password
// The user knows their password: prove it.
await client.api.password.change(newPassword, currentPassword);
// Following a reset link: the token in the session stands in for the old password.
await client.api.password.change(newPassword);The response is { success: boolean, message?: string } — so check success, do not assume a resolved
promise means it worked.
import { createAtomicClient } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
export async function changePassword(current: string, next: string) {
// A resolved promise does not mean success: the server reports it in the body.
const result = await client.api.password.change(next, current);
if (!result.success) {
// The message carries the server's own policy explanation — show it verbatim
// rather than guessing why it was refused.
throw new Error(result.message ?? "The password could not be changed.");
}
// The session survives the change; nothing to re-establish.
return result;
}import { ChangeDetectionStrategy, Component, inject, signal } from "@angular/core";
import { SINEQUA } from "./sinequa";
@Component({
selector: "app-change-password",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<form (submit)="submit($event)">
<input type="password" autocomplete="current-password" [value]="current()"
(input)="current.set(value($event))" />
<input type="password" autocomplete="new-password" [value]="next()"
(input)="next.set(value($event))" />
<button type="submit" [disabled]="busy() || !current() || !next()">Change</button>
@if (message()) {
<p role="alert">{{ message() }}</p>
}
</form>
`,
})
export class ChangePasswordComponent {
private readonly client = inject(SINEQUA);
readonly current = signal("");
readonly next = signal("");
readonly busy = signal(false);
readonly message = signal("");
async submit(event: Event) {
event.preventDefault();
this.busy.set(true);
try {
const result = await this.client.api.password.change(this.next(), this.current());
// A resolved promise does not mean success: the server says so in the body.
this.message.set(
result.success
? "Password changed."
: (result.message ?? "The password could not be changed."),
);
} finally {
this.busy.set(false);
}
}
value(event: Event) {
return (event.target as HTMLInputElement).value;
}
}import { type FormEvent, useState } from "react";
import { sinequa } from "./sinequa";
export function ChangePassword() {
const [current, setCurrent] = useState("");
const [next, setNext] = useState("");
const [message, setMessage] = useState("");
const [busy, setBusy] = useState(false);
async function submit(event: FormEvent) {
event.preventDefault();
setBusy(true);
try {
const result = await sinequa.api.password.change(next, current);
// A resolved promise does not mean success: the server says so in the body.
setMessage(
result.success ? "Password changed." : (result.message ?? "The password could not be changed."),
);
} finally {
setBusy(false);
}
}
return (
<form onSubmit={submit}>
<input
type="password"
autoComplete="current-password"
value={current}
onChange={(event) => setCurrent(event.target.value)}
/>
<input
type="password"
autoComplete="new-password"
value={next}
onChange={(event) => setNext(event.target.value)}
/>
<button type="submit" disabled={busy || !current || !next}>
Change
</button>
{message && <p role="alert">{message}</p>}
</form>
);
}Forgotten password
client.api.password.sendResetEmail(userName) asks the server to mail a reset link. The response carries
email — usually masked — and passwordResetTokenExpiry, which is worth showing so the user knows how long
they have.
const { email, passwordResetTokenExpiry } = await client.api.password.sendResetEmail(userName);
// Say where it went and how long it lasts, without confirming whether the account exists.
console.log(`If that account exists, a link was sent to ${email ?? "your address"}.`);This call crashes outside a browser
It builds its redirect URL from window.location.origin with no guard, so it throws ReferenceError: window is not defined in Node — in a server-rendered page, or a test running without a DOM. It is listed as a defect;
call it only from the browser until it takes the URL as a parameter.
Warning before expiry
principal.passwordExpirationDate lets you warn ahead of time rather than at the moment of lockout. Two
exported helpers read it:
import { expiresSoon, isExpired } from "@sinequa/atomic";
const date = principal.passwordExpirationDate ?? undefined;
if (isExpired(date)) promptChangeNow();
else if (expiresSoon(date, 7)) showBanner("Your password expires within a week.");Both tolerate undefined, which is what you get on a deployment that does not track expiry. expiresSoon
defaults to a seven-day window.