Skip to content

Commit

Permalink
Autostart the registration/authentication
Browse files Browse the repository at this point in the history
The gesture (buttonpress) is no longer required to start the WebAuthn
session with the browser. This is a revert of the original behavior. But
the button remained on the page for users of an old unsupportive
browser.

See https://www.pivotaltracker.com/story/show/184695105
  • Loading branch information
MKodde committed Jun 20, 2023
1 parent cbcb7b8 commit 50b2e72
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 19 deletions.
21 changes: 21 additions & 0 deletions public/typescript/observable/startAuthentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Observable } from 'rxjs';
import { reloadPage } from '../function';
import { PublicKeyResponseValidator } from '../function/http';
import { ApplicationAction, SerializedPublicKeyCredentialRequestOptions } from '../model';
import { authenticationObservable } from './index';
import { retryWith } from '../operator';

export const startAuthentication = (dispatch: (action: ApplicationAction) => void, publicKeyOptions: SerializedPublicKeyCredentialRequestOptions, send: PublicKeyResponseValidator, whenClicked: Observable<unknown>) => {
const time = () => (new Date()).toISOString();
return authenticationObservable(
send,
publicKeyOptions,
(options) => navigator.credentials.get(options),
time,
)
.pipe(retryWith((type) => (value) => dispatch({ type, value, timestamp: time() }), whenClicked))
.subscribe({
next: dispatch,
complete: reloadPage,
});
};
21 changes: 21 additions & 0 deletions public/typescript/observable/startRegistration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Observable } from 'rxjs';
import { reloadPage } from '../function';
import { PublicKeyResponseValidator } from '../function/http';
import { ApplicationAction, SerializedPublicKeyCredentialCreationOptions } from '../model';
import { registrationObservable } from './index';
import { retryWith } from '../operator';

export const startRegistration = (dispatch: (action: ApplicationAction) => void, publicKeyOptions: SerializedPublicKeyCredentialCreationOptions, send: PublicKeyResponseValidator, whenClicked: Observable<unknown>) => {
const time = () => (new Date()).toISOString();
return registrationObservable(
send,
publicKeyOptions,
(options) => navigator.credentials.create(options),
time,
)
.pipe(retryWith((type) => (value) => dispatch({ type, value, timestamp: time() }), whenClicked))
.subscribe({
next: dispatch,
complete: reloadPage,
});
};
12 changes: 10 additions & 2 deletions public/typescript/ui/component/AuthenticationContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { FC } from 'react';
import React, { FC, useEffect } from 'react';
import { RequestInformation, SerializedPublicKeyCredentialRequestOptions, TranslationString } from '../../model';
import { useAppReducer, useAuthenticationEffect, useClickable } from '../hook';
import { useVerifyPublicKeyCredentials } from '../hook/useVerifyPublicKeyCredentials';
import { App } from './App';
import { startAuthentication } from '../../observable/startAuthentication';
import { Subscription } from 'rxjs';

export interface AuthenticationContainerProps {
publicKeyOptions: SerializedPublicKeyCredentialRequestOptions;
Expand All @@ -17,5 +19,11 @@ export const AuthenticationContainer: FC<AuthenticationContainerProps> = ({ t, r
const [click, clicked] = useClickable();
const verify = useVerifyPublicKeyCredentials(responseUrl);
const onStart = useAuthenticationEffect(dispatch, publicKeyOptions, verify, clicked);
return <App started={started} startMessage="authentication.start_button" message={message} errorInfo={errorInfo} requestInformation={requestInformation} t={t} onClick={click} onStart={onStart} />;
useEffect(() => {
const subscription: Subscription = startAuthentication(dispatch, publicKeyOptions, verify, clicked);
return () => {
subscription.unsubscribe();
};
}, [dispatch, publicKeyOptions, verify, clicked]);
return <App started={started} startMessage="authentication.start_button" message={message} errorInfo={errorInfo} requestInformation={requestInformation} t={t} onClick={click} onStart={onStart}/>;
};
13 changes: 12 additions & 1 deletion public/typescript/ui/component/RegistrationContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { FC } from 'react';
import React, { FC, useEffect } from 'react';
import { RequestInformation, SerializedPublicKeyCredentialCreationOptions, TranslationString } from '../../model';
import { useAppReducer, useClickable, useRegistrationEffect } from '../hook';
import { useVerifyPublicKeyCredentials } from '../hook/useVerifyPublicKeyCredentials';
import { App } from './App';
import { Subscription } from 'rxjs';
import { startRegistration } from '../../observable/startRegistration';

export interface RegistrationContainerProps {
t: (key: TranslationString) => string;
Expand All @@ -12,10 +14,19 @@ export interface RegistrationContainerProps {
}

export const RegistrationContainer: FC<RegistrationContainerProps> = ({ t, responseUrl, publicKeyOptions, requestInformation }) => {

const [state, dispatch] = useAppReducer(requestInformation, 'status.registration_initial');
const { message, errorInfo, started } = state;
const [click, clicked] = useClickable();
const verify = useVerifyPublicKeyCredentials(responseUrl);
const onStart = useRegistrationEffect(dispatch, publicKeyOptions, verify, clicked);

useEffect(() => {
const subscription: Subscription = startRegistration(dispatch, publicKeyOptions, verify, clicked);
return () => {
subscription.unsubscribe();
};
}, [dispatch, publicKeyOptions, verify, clicked]);

return <App started={started} startMessage="registration.start_button" message={message} errorInfo={errorInfo} requestInformation={requestInformation} t={t} onClick={click} onStart={onStart} />;
};
19 changes: 3 additions & 16 deletions public/typescript/ui/hook/useRegistrationEffect.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import { useCallback } from 'react';
import { Observable } from 'rxjs';
import { reloadPage } from '../../function';
import { Observable, Subscription } from 'rxjs';
import { PublicKeyResponseValidator } from '../../function/http';
import { ApplicationAction, SerializedPublicKeyCredentialCreationOptions } from '../../model';
import { registrationObservable } from '../../observable';
import { retryWith } from '../../operator';
import { startRegistration } from '../../observable/startRegistration';

export const useRegistrationEffect = (dispatch: (action: ApplicationAction) => void, publicKeyOptions: SerializedPublicKeyCredentialCreationOptions, send: PublicKeyResponseValidator, whenClicked: Observable<unknown>) =>
useCallback(
() => {
const time = () => (new Date()).toISOString();
const subscription = registrationObservable(
send,
publicKeyOptions,
(options) => navigator.credentials.create(options),
time,
)
.pipe(retryWith((type) => (value) => dispatch({ type, value, timestamp: time() }), whenClicked))
.subscribe({
next: dispatch,
complete: reloadPage,
});
const subscription: Subscription = startRegistration(dispatch, publicKeyOptions, send, whenClicked);
return () => subscription.unsubscribe();
},
[dispatch, publicKeyOptions, send, whenClicked],
Expand Down

0 comments on commit 50b2e72

Please sign in to comment.