diff --git a/src/identity/interaction/email-password/EmailPasswordUtil.ts b/src/identity/interaction/email-password/EmailPasswordUtil.ts
index 467374794..7ace30de0 100644
--- a/src/identity/interaction/email-password/EmailPasswordUtil.ts
+++ b/src/identity/interaction/email-password/EmailPasswordUtil.ts
@@ -31,13 +31,16 @@ export function throwIdpInteractionError(error: unknown, prefilled: Record 0, 'Password required');
+ assert(
+ typeof password === 'string' && password.length > 0,
+ 'Please enter a password.',
+ );
assert(
typeof confirmPassword === 'string' && confirmPassword.length > 0,
- 'Password confirmation required',
+ 'Please confirm your password.',
);
assert(
password === confirmPassword,
- 'Password and confirmation do not match',
+ 'Your password and confirmation did not match.',
);
}
diff --git a/src/identity/interaction/email-password/handler/RegistrationHandler.ts b/src/identity/interaction/email-password/handler/RegistrationHandler.ts
index 0b125752d..5199bf7aa 100644
--- a/src/identity/interaction/email-password/handler/RegistrationHandler.ts
+++ b/src/identity/interaction/email-password/handler/RegistrationHandler.ts
@@ -193,14 +193,12 @@ export class RegistrationHandler extends HttpHandler {
*/
private async parseInput(request: HttpRequest): Promise {
const parsed = await getFormDataRequestBody(request);
- let prefilled: Record = {};
+ const prefilled: Record = {};
try {
- for (const key of Object.keys(parsed)) {
- if (Array.isArray(parsed[key])) {
- throw new Error(`Multiple values found for key ${key}`);
- }
+ for (const [ key, value ] of Object.entries(parsed)) {
+ assert(!Array.isArray(value), `Unexpected multiple values for ${key}.`);
+ prefilled[key] = value ? value.trim() : '';
}
- prefilled = parsed as Record;
return this.validateInput(prefilled);
} catch (err: unknown) {
throwIdpInteractionError(err, prefilled);
@@ -212,54 +210,38 @@ export class RegistrationHandler extends HttpHandler {
* Verifies that all the data combinations make sense.
*/
private validateInput(parsed: NodeJS.Dict): ParsedInput {
- const { email, password, confirmPassword, podName, webId, template, createWebId, register, createPod } = parsed;
+ const { email, password, confirmPassword, webId, podName, register, createPod, createWebId, template } = parsed;
- assert(typeof email === 'string' && email.length > 0 && emailRegex.test(email),
- 'A valid e-mail address is required');
+ // Parse email
+ assert(typeof email === 'string' && emailRegex.test(email), 'Please enter a valid e-mail address.');
- const result: ParsedInput = {
+ const validated: ParsedInput = {
email,
template,
+ register: Boolean(register) || Boolean(createWebId),
+ createPod: Boolean(createPod) || Boolean(createWebId),
createWebId: Boolean(createWebId),
- register: Boolean(register),
- createPod: Boolean(createPod),
};
+ assert(validated.register || validated.createPod, 'Please register for a WebID or create a Pod.');
- const validWebId = typeof webId === 'string' && webId.length > 0;
- if (result.createWebId) {
- if (validWebId) {
- throw new Error('A WebID should only be provided when no new one is being created');
- }
- } else {
- if (!validWebId) {
- throw new Error('A WebID is required if no new one is being created');
- }
- result.webId = webId;
+ // Parse WebID
+ if (!validated.createWebId) {
+ assert(typeof webId === 'string' && /^https?:\/\/[^/]+/u.test(webId), 'Please enter a valid WebID.');
+ validated.webId = webId;
}
- if (result.register) {
+ // Parse Pod name
+ if (validated.createWebId || validated.createPod) {
+ assert(typeof podName === 'string' && podName.length > 0, 'Please specify a Pod name.');
+ validated.podName = podName;
+ }
+
+ // Parse account
+ if (validated.register) {
assertPassword(password, confirmPassword);
- result.password = password;
- } else if (typeof password === 'string' && password.length > 0) {
- throw new Error('A password should only be provided when registering');
+ validated.password = password;
}
- if (result.createWebId || result.createPod) {
- assert(typeof podName === 'string' && podName.length > 0,
- 'A pod name is required when creating a pod and/or WebID');
- result.podName = podName;
- } else if (typeof podName === 'string' && podName.length > 0) {
- throw new Error('A pod name should only be provided when creating a pod and/or WebID');
- }
-
- if (result.createWebId && !(result.register && result.createPod)) {
- throw new Error('Creating a WebID is only possible when also registering and creating a pod');
- }
-
- if (!result.createWebId && !result.register && !result.createPod) {
- throw new Error('At least one option needs to be chosen');
- }
-
- return result;
+ return validated;
}
}
diff --git a/templates/identity/email-password/login.html.ejs b/templates/identity/email-password/login.html.ejs
index 382f03fee..75b9b4a7b 100644
--- a/templates/identity/email-password/login.html.ejs
+++ b/templates/identity/email-password/login.html.ejs
@@ -19,6 +19,7 @@
<% } %>
+ Your account
Email
diff --git a/templates/identity/email-password/register-response.html.ejs b/templates/identity/email-password/register-response.html.ejs
index 3bbe29e72..46dee30c7 100644
--- a/templates/identity/email-password/register-response.html.ejs
+++ b/templates/identity/email-password/register-response.html.ejs
@@ -19,9 +19,9 @@
<% if (createPod) { %>
- Your new pod
+ Your new Pod
- Your new pod is located at <%= podBaseUrl %> .
+ Your new Pod is located at <%= podBaseUrl %> .
You can store your documents and data there.
@@ -37,18 +37,18 @@
<% } %>
<% if (register) { %>
- Your new login
+ Your new account
- You can log in
- with your WebID <%= webId %>
- on this server via your email address <%= email %> .
+ Via your email address <%= email %> ,
+ this server lets you log in to Solid apps
+ with your WebID <%= webId %>
<% if (!createWebId) { %>
You will need to add the triple
<%= `<${webId}> <${oidcIssuer}>.`%>
to your existing WebID document <%= webId %>
- to indicate that you trust this server as a login provider for your WebID.
+ to indicate that you trust this server as a login provider.
<% } %>
<% } %>
diff --git a/templates/identity/email-password/register.html.ejs b/templates/identity/email-password/register.html.ejs
index 448ac5bf6..9ffb990ce 100644
--- a/templates/identity/email-password/register.html.ejs
+++ b/templates/identity/email-password/register.html.ejs
@@ -12,58 +12,195 @@
Community Solid Server
- Sign up for an account
-
+
+
diff --git a/templates/styles/main.css b/templates/styles/main.css
index 0a0c6058c..d6603c7d8 100644
--- a/templates/styles/main.css
+++ b/templates/styles/main.css
@@ -72,10 +72,14 @@ header img {
main h1 {
margin: 1em 0 0;
+
+ font-size: 2em;
font-weight: 700;
}
main h2 {
margin: 1em 0 0;
+
+ font-size: 1.5em;
font-weight: 600;
color: var(--solid-gray);
}
@@ -134,35 +138,49 @@ pre {
fieldset {
border: none;
margin: 0;
- padding: 0 0 0 1em;
+ padding: 0;
max-width: 600px;
}
+fieldset > legend {
+ margin: .5em 0 0;
+
+ font-size: 1.5em;
+ font-weight: 600;
+ color: var(--solid-gray);
+}
+fieldset > legend + p {
+ margin: 0 0 .5em .25em;
+}
fieldset ol {
list-style: none;
- margin; 0;
- padding: 0;
+ margin: 0;
+ padding: 0 0 0 2em;
}
fieldset ol ol {
- margin: 0 0 1em 2em;
+ margin: 0 0 1em .5em;
}
-fieldset ol li:not(.checkbox) {
+fieldset ol li:not(.checkbox, .radio) {
display: grid;
grid-template-columns: 10em auto;
grid-gap: 1em;
align-items: center;
}
-fieldset ol ol li:not(.checkbox) {
- grid-template-columns: 8em auto;
+fieldset ol ol li:not(.checkbox, .radio) {
+ grid-template-columns: 7.5em auto;
}
fieldset li label {
font-weight: 600;
}
-fieldset li:not(.checkbox) > label:after {
- content: ": ";
+fieldset li li label {
+ font-weight: 500;
}
-fieldset li.checkbox > label > input {
+fieldset li.checkbox > label > input,
+fieldset li.radio > label > input {
margin: 0 .5em 0 0;
}
+fieldset li p {
+ margin-top: .25em;
+}
input {
border: 1px solid var(--solid-purple);
@@ -183,7 +201,7 @@ button:hover {
}
form p.actions {
- margin: 0 0 1em 12em;
+ margin: .5em 0 1em 11em;
}
form p.error {
@@ -193,7 +211,7 @@ form p.error {
form ul.actions {
padding: 0;
- margin: 0 0 1em 12em;
+ margin: 0 0 1em 11em;
}
form ul.actions > li {
list-style-type: none;
@@ -201,6 +219,15 @@ form ul.actions > li {
margin-right: 1em;
}
+form.loaded * {
+ max-height: 500px;
+ transition: max-height .2s;
+}
+form .hidden {
+ max-height: 0;
+ overflow: hidden;
+}
+
ul.container > li {
margin: 0.25em 0;
list-style-type: none;
diff --git a/test/integration/Identity.test.ts b/test/integration/Identity.test.ts
index 203c40b5b..1e8903211 100644
--- a/test/integration/Identity.test.ts
+++ b/test/integration/Identity.test.ts
@@ -117,8 +117,8 @@ describe('A Solid server with IDP', (): void => {
const res = await postForm(`${baseUrl}idp/register`, formBody);
expect(res.status).toBe(200);
const text = await res.text();
- expect(text).toMatch(new RegExp(`your WebID.*${webId}`, 'u'));
- expect(text).toMatch(new RegExp(`your email address.*${email}`, 'u'));
+ expect(text).toMatch(new RegExp(`your.WebID.*${webId}`, 'u'));
+ expect(text).toMatch(new RegExp(`your.email.address.*${email}`, 'u'));
expect(text).toMatch(new RegExp(`<${webId}> <http://www.w3.org/ns/solid/terms#oidcIssuer> <${baseUrl}>\\.
`, 'mu'));
});
});
@@ -280,7 +280,7 @@ describe('A Solid server with IDP', (): void => {
const res = await postForm(`${baseUrl}idp/register`, formBody);
expect(res.status).toBe(200);
const text = await res.text();
- expect(text).toMatch(new RegExp(`Your new pod.*${baseUrl}${podName}/`, 'u'));
+ expect(text).toMatch(new RegExp(`Your new Pod.*${baseUrl}${podName}/`, 'u'));
});
});
@@ -305,7 +305,7 @@ describe('A Solid server with IDP', (): void => {
newWebId = matchWebId![1];
expect(text).toMatch(new RegExp(`new WebID is.*${newWebId}`, 'u'));
expect(text).toMatch(new RegExp(`your email address.*${newMail}`, 'u'));
- expect(text).toMatch(new RegExp(`Your new pod.*${baseUrl}${podName}/`, 'u'));
+ expect(text).toMatch(new RegExp(`Your new Pod.*${baseUrl}${podName}/`, 'u'));
});
it('initializes the session and logs in.', async(): Promise => {
diff --git a/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts b/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts
index 096140f70..3f74896ba 100644
--- a/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts
+++ b/test/unit/identity/interaction/email-password/EmailPasswordUtil.test.ts
@@ -53,12 +53,11 @@ describe('EmailPasswordUtil', (): void => {
describe('#assertPassword', (): void => {
it('validates the password against the confirmPassword.', async(): Promise => {
- expect((): void => assertPassword(undefined, undefined)).toThrow('Password required');
- expect((): void => assertPassword([], undefined)).toThrow('Password required');
- expect((): void => assertPassword('password', undefined)).toThrow('Password confirmation required');
- expect((): void => assertPassword('password', [])).toThrow('Password confirmation required');
- expect((): void => assertPassword('password', 'confirmPassword'))
- .toThrow('Password and confirmation do not match');
+ expect((): void => assertPassword(undefined, undefined)).toThrow('Please enter a password.');
+ expect((): void => assertPassword([], undefined)).toThrow('Please enter a password.');
+ expect((): void => assertPassword('password', undefined)).toThrow('Please confirm your password.');
+ expect((): void => assertPassword('password', [])).toThrow('Please confirm your password.');
+ expect((): void => assertPassword('password', 'other')).toThrow('Your password and confirmation did not match');
expect(assertPassword('password', 'password')).toBeUndefined();
});
});
diff --git a/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts b/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts
index 278ca1a1f..444b1122d 100644
--- a/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts
+++ b/test/unit/identity/interaction/email-password/handler/RegistrationHandler.test.ts
@@ -78,81 +78,73 @@ describe('A RegistrationHandler', (): void => {
describe('validating data', (): void => {
it('rejects array inputs.', async(): Promise => {
- request = createPostFormRequest({ data: [ 'a', 'b' ]});
- await expect(handler.handle({ request, response })).rejects.toThrow('Multiple values found for key data');
+ request = createPostFormRequest({ mydata: [ 'a', 'b' ]});
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Unexpected multiple values for mydata.');
});
it('errors on invalid emails.', async(): Promise => {
request = createPostFormRequest({ email: undefined });
- await expect(handler.handle({ request, response })).rejects.toThrow('A valid e-mail address is required');
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Please enter a valid e-mail address.');
request = createPostFormRequest({ email: '' });
- await expect(handler.handle({ request, response })).rejects.toThrow('A valid e-mail address is required');
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Please enter a valid e-mail address.');
request = createPostFormRequest({ email: 'invalidEmail' });
- await expect(handler.handle({ request, response })).rejects.toThrow('A valid e-mail address is required');
- });
-
- it('errors when an unnecessary WebID is provided.', async(): Promise => {
- request = createPostFormRequest({ email, webId, createWebId });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A WebID should only be provided when no new one is being created');
+ .rejects.toThrow('Please enter a valid e-mail address.');
});
it('errors when a required WebID is not valid.', async(): Promise => {
- request = createPostFormRequest({ email, webId: undefined });
+ request = createPostFormRequest({ email, register, webId: undefined });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A WebID is required if no new one is being created');
+ .rejects.toThrow('Please enter a valid WebID.');
- request = createPostFormRequest({ email, webId: '' });
+ request = createPostFormRequest({ email, register, webId: '' });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A WebID is required if no new one is being created');
- });
-
- it('errors when an unnecessary password is provided.', async(): Promise => {
- request = createPostFormRequest({ email, webId, password });
- await expect(handler.handle({ request, response }))
- .rejects.toThrow('A password should only be provided when registering');
+ .rejects.toThrow('Please enter a valid WebID.');
});
it('errors on invalid passwords when registering.', async(): Promise => {
request = createPostFormRequest({ email, webId, password, confirmPassword: 'bad', register });
- await expect(handler.handle({ request, response })).rejects.toThrow('Password and confirmation do not match');
- });
-
- it('errors when an unnecessary pod name is provided.', async(): Promise => {
- request = createPostFormRequest({ email, webId, podName });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A pod name should only be provided when creating a pod and/or WebID');
+ .rejects.toThrow('Your password and confirmation did not match.');
});
it('errors on invalid pod names when required.', async(): Promise => {
- request = createPostFormRequest({ email, podName: undefined, createWebId });
+ request = createPostFormRequest({ email, webId, createPod, podName: undefined });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A pod name is required when creating a pod and/or WebID');
+ .rejects.toThrow('Please specify a Pod name.');
- request = createPostFormRequest({ email, webId, podName: '', createPod });
+ request = createPostFormRequest({ email, webId, createPod, podName: ' ' });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('A pod name is required when creating a pod and/or WebID');
+ .rejects.toThrow('Please specify a Pod name.');
+
+ request = createPostFormRequest({ email, webId, createWebId });
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Please specify a Pod name.');
});
it('errors when trying to create a WebID without registering or creating a pod.', async(): Promise => {
request = createPostFormRequest({ email, podName, createWebId });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('Creating a WebID is only possible when also registering and creating a pod');
-
- request = createPostFormRequest({ email, podName, password, confirmPassword, createWebId, register });
- await expect(handler.handle({ request, response }))
- .rejects.toThrow('Creating a WebID is only possible when also registering and creating a pod');
+ .rejects.toThrow('Please enter a password.');
request = createPostFormRequest({ email, podName, createWebId, createPod });
await expect(handler.handle({ request, response }))
- .rejects.toThrow('Creating a WebID is only possible when also registering and creating a pod');
+ .rejects.toThrow('Please enter a password.');
+
+ request = createPostFormRequest({ email, podName, createWebId, createPod, register });
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Please enter a password.');
});
it('errors when no option is chosen.', async(): Promise => {
request = createPostFormRequest({ email, webId });
- await expect(handler.handle({ request, response })).rejects.toThrow('At least one option needs to be chosen');
+ await expect(handler.handle({ request, response }))
+ .rejects.toThrow('Please register for a WebID or create a Pod.');
});
});
diff --git a/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts b/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts
index d9ff3cab0..19cd6ca6b 100644
--- a/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts
+++ b/test/unit/identity/interaction/email-password/handler/ResetPasswordHandler.test.ts
@@ -43,7 +43,7 @@ describe('A ResetPasswordHandler', (): void => {
});
it('errors for invalid passwords.', async(): Promise => {
- const errorMessage = 'Password and confirmation do not match';
+ const errorMessage = 'Your password and confirmation did not match.';
request = createPostFormRequest({ password: 'password!', confirmPassword: 'otherPassword!' }, url);
await expect(handler.handle({ request, response })).rejects.toThrow(errorMessage);
});