-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
ProtocolUtils.ts
99 lines (86 loc) · 3.52 KB
/
ProtocolUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { StringUtils } from "./StringUtils";
import { Constants } from "./Constants";
import { ICrypto } from "../crypto/ICrypto";
import { ClientAuthError } from "../error/ClientAuthError";
/**
* Type which defines the object that is stringified, encoded and sent in the state value.
* Contains the following:
* - id - unique identifier for this request
* - ts - timestamp for the time the request was made. Used to ensure that token expiration is not calculated incorrectly.
* - platformState - string value sent from the platform.
*/
export type LibraryStateObject = {
id: string,
meta?: Record<string, string>
};
/**
* Type which defines the stringified and encoded object sent to the service in the authorize request.
*/
export type RequestStateObject = {
userRequestState: string,
libraryState: LibraryStateObject
};
/**
* Class which provides helpers for OAuth 2.0 protocol specific values
*/
export class ProtocolUtils {
/**
* Appends user state with random guid, or returns random guid.
* @param userState
* @param randomGuid
*/
static setRequestState(cryptoObj: ICrypto, userState?: string, meta?: Record<string, string>): string {
const libraryState = ProtocolUtils.generateLibraryState(cryptoObj, meta);
return !StringUtils.isEmpty(userState) ? `${libraryState}${Constants.RESOURCE_DELIM}${userState}` : libraryState;
}
/**
* Generates the state value used by the common library.
* @param randomGuid
* @param cryptoObj
*/
static generateLibraryState(cryptoObj: ICrypto, meta?: Record<string, string>): string {
if (!cryptoObj) {
throw ClientAuthError.createNoCryptoObjectError("generateLibraryState");
}
// Create a state object containing a unique id and the timestamp of the request creation
const stateObj: LibraryStateObject = {
id: cryptoObj.createNewGuid()
};
if (meta) {
stateObj.meta = meta;
}
const stateString = JSON.stringify(stateObj);
return cryptoObj.base64Encode(stateString);
}
/**
* Parses the state into the RequestStateObject, which contains the LibraryState info and the state passed by the user.
* @param state
* @param cryptoObj
*/
static parseRequestState(cryptoObj: ICrypto, state: string): RequestStateObject {
if (!cryptoObj) {
throw ClientAuthError.createNoCryptoObjectError("parseRequestState");
}
if (StringUtils.isEmpty(state)) {
throw ClientAuthError.createInvalidStateError(state, "Null, undefined or empty state");
}
try {
// Split the state between library state and user passed state and decode them separately
const splitState = state.split(Constants.RESOURCE_DELIM);
const libraryState = splitState[0];
const userState = splitState.length > 1 ? splitState.slice(1).join(Constants.RESOURCE_DELIM) : Constants.EMPTY_STRING;
const libraryStateString = cryptoObj.base64Decode(libraryState);
const libraryStateObj = JSON.parse(libraryStateString) as LibraryStateObject;
return {
userRequestState: !StringUtils.isEmpty(userState) ? userState : Constants.EMPTY_STRING,
libraryState: libraryStateObj
};
} catch(e) {
throw ClientAuthError.createInvalidStateError(state, e);
}
}
}