@microsoft/useragent-sdk
Version:
SDK for building decentralized identity wallets and enterprise agents.
191 lines (161 loc) • 5.95 kB
text/typescript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Identifier from '../Identifier';
import UserAgentError from '../UserAgentError';
import ClaimObject from '../credentials/ClaimObject';
import OidcResponse from './OidcResponse';
import OIDCAuthenticationRequest from '../crypto/protocols/did/requests/OIDCAuthenticationRequest';
import CryptoOptions from '../CryptoOptions';
import CryptoFactory from '../crypto/plugin/CryptoFactory';
import JwsToken from '../crypto/protocols/jose/jws/JwsToken';
import UserAgentOptions from '../UserAgentOptions';
import HttpResolver from '../resolvers/HttpResolver';
import VerifyHelper from './VerifyHelper';
/**
* Optional Parameters to add to OIDCRequests.
*/
export type OptionalOIDCRequestParams = {
/**
* Opaque value to represent state on serverside.
*/
state?: string;
/**
* ClaimObject if OIDC Request involves issuance of credential.
*/
claimObject?: ClaimObject;
/**
* Claim Requests if requesting to be presented with credential.
*/
claimsRequested?: string[];
};
/**
* Standard response type for SIOP.
*/
const responseType = 'id_token'
/**
* Standard response mode for SIOP.
*/
const responseMode = 'form_post'
/**
* Standard scope for SIOP.
*/
const scope = 'openid did_authn'
/**
* Class to represent Open ID Connect Self-Issued Tokens
* @class
*/
export default class OidcRequest {
/**
* Redirect URL for SIOP.
*/
public redirectUrl: string;
/**
* Nonce for SIOP.
*/
public nonce: string;
/**
* Sender of the request.
*/
public sender: Identifier;
/**
* Optional claimObject attached to OIDC request.
*/
public claimObject?: ClaimObject;
/**
* Opaque value to represent state on serverside.
*/
public state?: string;
/**
* Optional Claim that is requested for presentation.
*/
public claimsRequested?: string[];
/**
* Instantiates an self-signed OIDC Request.
* @param sender Identifier who will sign request.
* @param redirectUrl Redirect URL for SIOP.
* @param nonce Nonce for SIOP.
* @param claimObject optional claimObject to attach to request.
*/
constructor(sender: Identifier, redirectUrl: string, nonce: string, options?: OptionalOIDCRequestParams) {
this.sender = sender;
this.redirectUrl = redirectUrl;
this.nonce = nonce;
this.claimObject = options!.claimObject;
this.state = options!.state;
this.claimsRequested = options!.claimsRequested;
}
/**
* Forms the request to spec and sign the request.
* @param keyReference
* @returns jwt in compact form.
*/
public async sign(keyReference: string): Promise<string> {
const request: Partial<OIDCAuthenticationRequest> = {
iss: this.sender.id,
response_type: responseType,
response_mode: responseMode,
client_id: this.redirectUrl,
scope,
nonce: this.nonce
}
if (this.state) {
Object.assign(request, {state: this.state});
}
if (this.claimsRequested) {
const claimsRequestIdToken: {[key: string]: any} = {};
// Assuming every claim requested is essential for now.
this.claimsRequested.forEach(claimRef => {
claimsRequestIdToken[claimRef] = {
essential: true
}
});
Object.assign(request, {claims: {id_token: claimsRequestIdToken}});
}
if (this.claimObject) {
Object.assign(request, {offer: JSON.parse(this.claimObject.serialize())});
}
return this.sender.sign(request, keyReference);
}
/**
* Parses and Verifies signed JWT.
* @param signedRequest signed JWT containing OIDC request.
* @returns OidcRequest Object if verified.
*/
public static async verifyAndParse(signedRequest: string, cryptoFactory?: CryptoFactory): Promise<OidcRequest> {
if (!cryptoFactory) {
cryptoFactory = new CryptoOptions().cryptoFactory;
}
// get identifier id from key id in header.
const token : JwsToken = await JwsToken.deserialize(signedRequest, {cryptoFactory});
const request = JSON.parse(token.payload.toString());
const senderId = request.iss;
const options = new UserAgentOptions();
options.resolver = new HttpResolver('https://beta.discover.did.microsoft.com');
const sender = new Identifier(senderId, options)
// if (!await VerifyHelper.verify(sender, token)) {
console.log('not verifying for now');
// throw new UserAgentError(`Invalid signature for token issued by: ${request.iss}`);
// }
const requestOptions: OptionalOIDCRequestParams = {
state: request.state,
claimObject: request.offer,
};
// parse out requested claims to list if param is present.
if (request.claims && request.claims.id_token) {
const claimsRequested = Object.keys(request.claims.id_token);
Object.assign(requestOptions, {claimsRequested});
}
return new OidcRequest(sender, request.client_id, request.nonce, requestOptions);
}
/**
* Respond to OIDC Request using identifier on the client side.
* @param identifier the identifier used to sign response
* @returns the body of the HTTP response if receive status 200.
*/
public async respondWith(identifier: Identifier, keyReference: string, claimObjects?: ClaimObject[]): Promise<string> {
const oidcResponse = await OidcResponse.create(this, identifier);
return oidcResponse.signAndSend(keyReference, claimObjects);
}
}