Skip to content

Commit

Permalink
feat: password change HP-2289 (#371)
Browse files Browse the repository at this point in the history
* feat: password change
  • Loading branch information
Riippi authored Aug 8, 2024
1 parent 14c4ae5 commit 66c7b7a
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 44 deletions.
8 changes: 8 additions & 0 deletions src/auth/__tests__/authService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,4 +535,12 @@ describe('authService', () => {
});
});
});

describe('changePassword', () => {
it('should resolve if signinRedirect is successful', async () => {
vi.spyOn(userManager, 'signinRedirect').mockResolvedValueOnce();

await expect(authService.changePassword()).resolves.toBeUndefined();
});
});
});
18 changes: 18 additions & 0 deletions src/auth/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,24 @@ export class AuthService {
sessionStorage.removeItem(API_TOKEN);
this.userSessionValidityPoller.stop();
}

public async changePassword(): Promise<void> {
let success = true;
await this.userManager
.signinRedirect({
ui_locales: i18n.language,
extraQueryParams: {
kc_action: 'UPDATE_PASSWORD',
},
})
.catch(error => {
success = false;
if (error.message !== 'Network Error') {
Sentry.captureException(error);
}
});
return success ? Promise.resolve() : Promise.reject();
}
}

export default new AuthService();
2 changes: 2 additions & 0 deletions src/common/test/myProfileMocking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Addresses,
Phones,
InsertableNode,
LoginMethodType,
} from '../../graphql/typings';
import {
AdditionalInformationSource,
Expand Down Expand Up @@ -185,6 +186,7 @@ export const getMyProfile = (): ProfileRoot => ({
__typename: 'PhoneNodeConnection',
},
verifiedPersonalInformation: null,
loginMethods: [LoginMethodType.PASSWORD],
__typename: 'ProfileNode',
},
});
Expand Down
13 changes: 11 additions & 2 deletions src/graphql/generatedTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ export enum Language {
SWEDISH = 'SWEDISH'
}

export enum LoginMethodType {
/** One-time password */
OTP = 'OTP',
/** Salasana */
PASSWORD = 'PASSWORD',
/** Suomi.fi */
SUOMI_FI = 'SUOMI_FI'
}

export enum PhoneType {
/** Home phone */
HOME = 'HOME',
Expand Down Expand Up @@ -524,12 +533,12 @@ export type MyProfileQueryEmailsFragment = { readonly __typename: 'EmailNodeConn

export type MyProfileQueryPhonesFragment = { readonly __typename: 'PhoneNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'PhoneNodeEdge', readonly node: { readonly __typename: 'PhoneNode', readonly primary: boolean, readonly id: string, readonly phone: string, readonly phoneType: PhoneType | null } | null } | null> };

export type MyProfileQueryFragment = { readonly __typename: 'ProfileNode', readonly id: string, readonly firstName: string, readonly lastName: string, readonly nickname: string, readonly language: Language | null, readonly primaryAddress: { readonly __typename: 'AddressNode', readonly id: string, readonly primary: boolean, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null, readonly addresses: { readonly __typename: 'AddressNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'AddressNodeEdge', readonly node: { readonly __typename: 'AddressNode', readonly primary: boolean, readonly id: string, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null } | null> } | null, readonly primaryEmail: { readonly __typename: 'EmailNode', readonly id: string, readonly email: string, readonly primary: boolean, readonly emailType: EmailType | null } | null, readonly emails: { readonly __typename: 'EmailNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'EmailNodeEdge', readonly node: { readonly __typename: 'EmailNode', readonly primary: boolean, readonly id: string, readonly email: string, readonly emailType: EmailType | null } | null } | null> } | null, readonly primaryPhone: { readonly __typename: 'PhoneNode', readonly id: string, readonly phone: string, readonly primary: boolean, readonly phoneType: PhoneType | null } | null, readonly phones: { readonly __typename: 'PhoneNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'PhoneNodeEdge', readonly node: { readonly __typename: 'PhoneNode', readonly primary: boolean, readonly id: string, readonly phone: string, readonly phoneType: PhoneType | null } | null } | null> } | null, readonly verifiedPersonalInformation: { readonly __typename: 'VerifiedPersonalInformationNode', readonly firstName: string, readonly lastName: string, readonly givenName: string, readonly nationalIdentificationNumber: string, readonly municipalityOfResidence: string, readonly municipalityOfResidenceNumber: string, readonly permanentAddress: { readonly __typename: 'VerifiedPersonalInformationAddressNode', readonly streetAddress: string, readonly postalCode: string, readonly postOffice: string } | null, readonly permanentForeignAddress: { readonly __typename: 'VerifiedPersonalInformationForeignAddressNode', readonly streetAddress: string, readonly additionalAddress: string, readonly countryCode: string } | null } | null };
export type MyProfileQueryFragment = { readonly __typename: 'ProfileNode', readonly id: string, readonly firstName: string, readonly lastName: string, readonly nickname: string, readonly language: Language | null, readonly loginMethods: ReadonlyArray<LoginMethodType | null> | null, readonly primaryAddress: { readonly __typename: 'AddressNode', readonly id: string, readonly primary: boolean, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null, readonly addresses: { readonly __typename: 'AddressNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'AddressNodeEdge', readonly node: { readonly __typename: 'AddressNode', readonly primary: boolean, readonly id: string, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null } | null> } | null, readonly primaryEmail: { readonly __typename: 'EmailNode', readonly id: string, readonly email: string, readonly primary: boolean, readonly emailType: EmailType | null } | null, readonly emails: { readonly __typename: 'EmailNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'EmailNodeEdge', readonly node: { readonly __typename: 'EmailNode', readonly primary: boolean, readonly id: string, readonly email: string, readonly emailType: EmailType | null } | null } | null> } | null, readonly primaryPhone: { readonly __typename: 'PhoneNode', readonly id: string, readonly phone: string, readonly primary: boolean, readonly phoneType: PhoneType | null } | null, readonly phones: { readonly __typename: 'PhoneNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'PhoneNodeEdge', readonly node: { readonly __typename: 'PhoneNode', readonly primary: boolean, readonly id: string, readonly phone: string, readonly phoneType: PhoneType | null } | null } | null> } | null, readonly verifiedPersonalInformation: { readonly __typename: 'VerifiedPersonalInformationNode', readonly firstName: string, readonly lastName: string, readonly givenName: string, readonly nationalIdentificationNumber: string, readonly municipalityOfResidence: string, readonly municipalityOfResidenceNumber: string, readonly permanentAddress: { readonly __typename: 'VerifiedPersonalInformationAddressNode', readonly streetAddress: string, readonly postalCode: string, readonly postOffice: string } | null, readonly permanentForeignAddress: { readonly __typename: 'VerifiedPersonalInformationForeignAddressNode', readonly streetAddress: string, readonly additionalAddress: string, readonly countryCode: string } | null } | null };

export type MyProfileQueryVariables = Exact<{ [key: string]: never; }>;


export type MyProfileQuery = { readonly myProfile: { readonly __typename: 'ProfileNode', readonly id: string, readonly firstName: string, readonly lastName: string, readonly nickname: string, readonly language: Language | null, readonly primaryAddress: { readonly __typename: 'AddressNode', readonly id: string, readonly primary: boolean, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null, readonly addresses: { readonly __typename: 'AddressNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'AddressNodeEdge', readonly node: { readonly __typename: 'AddressNode', readonly primary: boolean, readonly id: string, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null } | null> } | null, readonly primaryEmail: { readonly __typename: 'EmailNode', readonly id: string, readonly email: string, readonly primary: boolean, readonly emailType: EmailType | null } | null, readonly emails: { readonly __typename: 'EmailNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'EmailNodeEdge', readonly node: { readonly __typename: 'EmailNode', readonly primary: boolean, readonly id: string, readonly email: string, readonly emailType: EmailType | null } | null } | null> } | null, readonly primaryPhone: { readonly __typename: 'PhoneNode', readonly id: string, readonly phone: string, readonly primary: boolean, readonly phoneType: PhoneType | null } | null, readonly phones: { readonly __typename: 'PhoneNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'PhoneNodeEdge', readonly node: { readonly __typename: 'PhoneNode', readonly primary: boolean, readonly id: string, readonly phone: string, readonly phoneType: PhoneType | null } | null } | null> } | null, readonly verifiedPersonalInformation: { readonly __typename: 'VerifiedPersonalInformationNode', readonly firstName: string, readonly lastName: string, readonly givenName: string, readonly nationalIdentificationNumber: string, readonly municipalityOfResidence: string, readonly municipalityOfResidenceNumber: string, readonly permanentAddress: { readonly __typename: 'VerifiedPersonalInformationAddressNode', readonly streetAddress: string, readonly postalCode: string, readonly postOffice: string } | null, readonly permanentForeignAddress: { readonly __typename: 'VerifiedPersonalInformationForeignAddressNode', readonly streetAddress: string, readonly additionalAddress: string, readonly countryCode: string } | null } | null } | null };
export type MyProfileQuery = { readonly myProfile: { readonly __typename: 'ProfileNode', readonly id: string, readonly firstName: string, readonly lastName: string, readonly nickname: string, readonly language: Language | null, readonly loginMethods: ReadonlyArray<LoginMethodType | null> | null, readonly primaryAddress: { readonly __typename: 'AddressNode', readonly id: string, readonly primary: boolean, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null, readonly addresses: { readonly __typename: 'AddressNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'AddressNodeEdge', readonly node: { readonly __typename: 'AddressNode', readonly primary: boolean, readonly id: string, readonly address: string, readonly postalCode: string, readonly city: string, readonly countryCode: string, readonly addressType: AddressType | null } | null } | null> } | null, readonly primaryEmail: { readonly __typename: 'EmailNode', readonly id: string, readonly email: string, readonly primary: boolean, readonly emailType: EmailType | null } | null, readonly emails: { readonly __typename: 'EmailNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'EmailNodeEdge', readonly node: { readonly __typename: 'EmailNode', readonly primary: boolean, readonly id: string, readonly email: string, readonly emailType: EmailType | null } | null } | null> } | null, readonly primaryPhone: { readonly __typename: 'PhoneNode', readonly id: string, readonly phone: string, readonly primary: boolean, readonly phoneType: PhoneType | null } | null, readonly phones: { readonly __typename: 'PhoneNodeConnection', readonly edges: ReadonlyArray<{ readonly __typename: 'PhoneNodeEdge', readonly node: { readonly __typename: 'PhoneNode', readonly primary: boolean, readonly id: string, readonly phone: string, readonly phoneType: PhoneType | null } | null } | null> } | null, readonly verifiedPersonalInformation: { readonly __typename: 'VerifiedPersonalInformationNode', readonly firstName: string, readonly lastName: string, readonly givenName: string, readonly nationalIdentificationNumber: string, readonly municipalityOfResidence: string, readonly municipalityOfResidenceNumber: string, readonly permanentAddress: { readonly __typename: 'VerifiedPersonalInformationAddressNode', readonly streetAddress: string, readonly postalCode: string, readonly postOffice: string } | null, readonly permanentForeignAddress: { readonly __typename: 'VerifiedPersonalInformationForeignAddressNode', readonly streetAddress: string, readonly additionalAddress: string, readonly countryCode: string } | null } | null } | null };

export type NameQueryVariables = Exact<{ [key: string]: never; }>;

Expand Down
1 change: 1 addition & 0 deletions src/graphql/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export {
AddressType,
Language,
TranslationLanguage,
LoginMethodType,
} from './generatedTypes';

export type AnyObject<T = unknown> = Record<string, T>;
33 changes: 18 additions & 15 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"title": "Do you want to delete your information?",
"loadingServices": "Searching for services connected to the profile",
"deleteSuccessful": "The Helsinki profile and all the data we hold on you have been deleted successfully!",
"deleteFailed": "The deletion failed for some reason. Please try again after a while!",
"deleteInfoforLightAuthentication": "You have services linked to your profile for which you have used strong identification. Please log out and log in again using Suomi.fi-authentication to delete your data."
"deleteFailed": "The deletion failed for some reason. Please try again after a while.",
"deleteInfoforLightAuthentication": "You have services linked to your profile for which you have used strong identification. Please log out and log in again using Suomi.fi authentication to delete your data."
},
"deleteProfileModal": {
"delete": "Delete my information",
Expand All @@ -62,7 +62,7 @@
"button": "Download my information (.json)",
"panelText": "Here you can download a file containing all the data related to you. The file contains all the data about you stored in the Helsinki profile and information about all the associated e-services.",
"panelTitle": "Do you want to download your information?",
"extrapanelTextforLightAuthentication": "You have services linked to your profile for which you have used strong identification. Please log out and log back in with Suomi.fi-authentication to download your data."
"extrapanelTextforLightAuthentication": "You have services linked to your profile for which you have used strong identification. Please log out and log back in with Suomi.fi authentication to download your data."
},
"expandingPanel": {
"closeButtonText": "Close",
Expand Down Expand Up @@ -101,9 +101,9 @@
},
"loading": "Loading...",
"login": {
"description": "A Helsinki profile will be created for you when you log in to City of Helsinki services. Through your profile, you can view and manage your data and how it is used in different services.",
"description": "A Helsinki profile will be created for you when you log in to City of Helsinki services. Through your profile, you can manage your data and view how it is used in different services.",
"login": "Log in",
"title": "Your profile information in one address!"
"title": "Your profile information at one address!"
},
"nav": {
"information": "Information",
Expand Down Expand Up @@ -188,19 +188,22 @@
"ariaShowOptions": "Show options",
"ariaSelectedOption": "{{value}} is selected",
"ariaNoSelectedItemForLabel": "{{label}} is not selected",
"changePassword": "Change password",
"deleteProfile": "Delete your profile",
"description": "The data stored in the Helsinki profile is used in the City of Helsinki’s e-services. You can find more detailed information on the <linkToServices>{{linkToServicesText}} page</linkToServices>.",
"description": "The data stored in the Helsinki profile is used in the City of Helsinki’s e-services. You can find more detailed information on the <linkToServices>{{linkToServicesText}}</linkToServices> page.",
"downloadData": "Download your data",
"email": "Email",
"loginAndAuthentication": "Login and authentication",
"name": "Name",
"password": "Password",
"personalData": "Personal data",
"phone": "Phone number",
"title": "My information",
"visibility": "The following information about you is stored in Helsinki profile.",
"authenticationMethod": "Authentication method",
"permanentAddress": "Permanent address",
"permanentForeignAddress": "Permanent foreign address",
"verifiedDataInformation": "You can only see the official information when you have logged in using strong authentication via the Suomi.fi service. The information has been retrieved from The National Population Information System and cannot be edited via your Helsinki profile. You can check the data stored about you in the Population Information System in the <suomiFiLink>suomi.fi -web service</suomiFiLink>.",
"verifiedDataInformation": "You can only see the official information when you have logged in using strong authentication via the Suomi.fi service. The information has been retrieved from The National Population Information System and cannot be edited via your Helsinki profile. You can check the data stored about you in the Population Information System in the <suomiFiLink>Suomi.fi web service</suomiFiLink>.",
"verifiedDataInformationLink": "https://www.suomi.fi/",
"verifiedData": "Verified data",
"verifiedBasicData": "Official information",
Expand Down Expand Up @@ -239,7 +242,7 @@
"connectionRemovalVerificationButtonText": "Delete data",
"connectionRemovalError": "For some reason, the deletion was not successful. Please, try again later!",
"contactServiceToDelete": "If you still want to delete the data, please contact <linkToExternalServiceList >the service</linkToExternalServiceList> directly and then retry deleting the data.",
"explanationforLightAuthentication": "You can only delete data from this service if you are strongly identified. Please log out and log in again using Suomi.fi-identification to delete your data."
"explanationforLightAuthentication": "You can only delete data from this service if you are strongly identified. Please log out and log in again using Suomi.fi identification to delete your data."
},
"skipToContent": "Skip to content",
"validation": {
Expand All @@ -253,12 +256,12 @@
"requiredFieldIndication": "Fields marked with an * are compulsory."
},
"identityProvider": {
"helsinkiAccount": "Helsinki Profile user ID",
"github": "GitHub",
"google": "Google",
"facebook": "Facebook",
"yletunnus": "Yle",
"tunnistusSuomifi": "Suomi.fi"
"helsinkiAccount": "You are authenticated with Helsinki ID.",
"github": "You are authenticated with GitHub account.",
"google": "You are authenticated with Google account.",
"facebook": "You are authenticated with Facebook account.",
"yletunnus": "You are authenticated with Yle ID.",
"tunnistusSuomifi": "You are authenticated with Suomi.fi e-Identification."
},
"opensInNewWindow": "{{title}}. Opens in a new window.",
"openInNewTabAriaLabel": "Opens in a new tab.",
Expand All @@ -285,7 +288,7 @@
"consentStorageDescription": "Cookie stores consent for using analytics cookies."
},
"changeProfilePassword ": {
"explanationForStrongAuthentication": "You can only change your password if you are strongly identified. Log out and log in again using Suomi.fi-authentication to change your password."
"explanationForStrongAuthentication": "You can only change your password if you are strongly identified. Log out and log in again using Suomi.fi authentication to change your password."
},
"userGuide": "Helsinki profile guide"
}
Loading

0 comments on commit 66c7b7a

Please sign in to comment.