feat: Let InteractionCompleter return redirect URL

This commit is contained in:
Joachim Van Herwegen
2021-08-06 15:26:25 +02:00
parent 7b7040a196
commit 7b42c72142
7 changed files with 72 additions and 25 deletions

View File

@@ -1,6 +1,7 @@
import urljoin from 'url-join';
import type { ErrorHandler } from '../ldp/http/ErrorHandler';
import type { RequestParser } from '../ldp/http/RequestParser';
import { RedirectResponseDescription } from '../ldp/http/response/RedirectResponseDescription';
import type { ResponseWriter } from '../ldp/http/ResponseWriter';
import type { Operation } from '../ldp/operations/Operation';
import type { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences';
@@ -15,11 +16,9 @@ import { assertError, createErrorMessage } from '../util/errors/ErrorUtil';
import { InternalServerError } from '../util/errors/InternalServerError';
import { trimTrailingSlashes } from '../util/PathUtil';
import type { ProviderFactory } from './configuration/ProviderFactory';
import type {
Interaction,
import type { Interaction,
InteractionHandler,
InteractionHandlerResult,
} from './interaction/email-password/handler/InteractionHandler';
InteractionHandlerResult } from './interaction/email-password/handler/InteractionHandler';
import { IdpInteractionError } from './interaction/util/IdpInteractionError';
import type { InteractionCompleter } from './interaction/util/InteractionCompleter';
@@ -159,7 +158,8 @@ export class IdentityProviderHttpHandler extends HttpHandler {
);
}
// We need the original request object for the OIDC library
return await this.interactionCompleter.handleSafe({ ...result.details, request, response });
const location = await this.interactionCompleter.handleSafe({ ...result.details, request });
return await this.responseWriter.handleSafe({ response, result: new RedirectResponseDescription(location) });
}
if (result.type === 'response' && templateFile) {
return await this.handleTemplateResponse(response, templateFile, result.details, oidcInteraction);

View File

@@ -1,19 +1,26 @@
import { ServerResponse } from 'http';
import type { InteractionResults } from 'oidc-provider';
import type { HttpHandlerInput } from '../../../server/HttpHandler';
import type { HttpRequest } from '../../../server/HttpRequest';
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
import type { ProviderFactory } from '../../configuration/ProviderFactory';
/**
* Parameters required to specify how the interaction should be completed.
*/
export interface InteractionCompleterParams {
webId: string;
shouldRemember?: boolean;
}
export type InteractionCompleterInput = HttpHandlerInput & InteractionCompleterParams;
export interface InteractionCompleterInput extends InteractionCompleterParams {
request: HttpRequest;
}
/**
* Completes an IDP interaction, logging the user in.
* Returns the URL the request should be redirected to.
*/
export class InteractionCompleter extends AsyncHandler<InteractionCompleterInput> {
export class InteractionCompleter extends AsyncHandler<InteractionCompleterInput, string> {
private readonly providerFactory: ProviderFactory;
public constructor(providerFactory: ProviderFactory) {
@@ -21,7 +28,7 @@ export class InteractionCompleter extends AsyncHandler<InteractionCompleterInput
this.providerFactory = providerFactory;
}
public async handle(input: InteractionCompleterInput): Promise<void> {
public async handle(input: InteractionCompleterInput): Promise<string> {
const provider = await this.providerFactory.getProvider();
const result: InteractionResults = {
login: {
@@ -34,6 +41,9 @@ export class InteractionCompleter extends AsyncHandler<InteractionCompleterInput
},
};
return provider.interactionFinished(input.request, input.response, result);
// Response object is not actually needed here so we can just mock it like this
// to bypass the OIDC library checks.
// See https://github.com/panva/node-oidc-provider/discussions/1078
return provider.interactionResult(input.request, Object.create(ServerResponse.prototype), result);
}
}

View File

@@ -5,7 +5,7 @@ import type { ResourceIdentifier } from '../../representation/ResourceIdentifier
import { ResponseDescription } from './ResponseDescription';
/**
* Corresponds to a 201 response, containing the relevant link metadata.
* Corresponds to a 201 response, containing the relevant location metadata.
*/
export class CreatedResponseDescription extends ResponseDescription {
public constructor(location: ResourceIdentifier) {

View File

@@ -0,0 +1,14 @@
import { DataFactory } from 'n3';
import { SOLID_HTTP } from '../../../util/Vocabularies';
import { RepresentationMetadata } from '../../representation/RepresentationMetadata';
import { ResponseDescription } from './ResponseDescription';
/**
* Corresponds to a 301/302 response, containing the relevant location metadata.
*/
export class RedirectResponseDescription extends ResponseDescription {
public constructor(location: string, permanently = false) {
const metadata = new RepresentationMetadata({ [SOLID_HTTP.location]: DataFactory.namedNode(location) });
super(permanently ? 301 : 302, metadata);
}
}