Skip to content

Commit

Permalink
Merge pull request #2157 from AzureAD/msal-node/on-behalf-of
Browse files Browse the repository at this point in the history
[msal-node][msal-common] Support for On-behalf-of flow
  • Loading branch information
sangonzal committed Sep 9, 2020
2 parents 8ef3db1 + c91d0f7 commit 126b83b
Show file tree
Hide file tree
Showing 38 changed files with 2,236 additions and 47 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ FakesAssemblies/
# Visual Studio 6 workspace options file
*.opt

# VS Code History Plugin
.history

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Add support for On-behalf-of flow",
"packageName": "@azure/msal-common",
"email": "sagonzal@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-08-20T19:21:21.628Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "Add support for on-behalf-of flow",
"packageName": "@azure/msal-node",
"email": "sagonzal@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-08-20T19:21:33.794Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Add on-behalf-of sample",
"packageName": "msal-node-on-behal-of-web-api",
"email": "sagonzal@microsoft.com",
"dependentChangeType": "none",
"date": "2020-08-20T19:22:16.698Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "none",
"comment": "Add on-behalf-of sample",
"packageName": "msal-node-on-behal-of-web-app",
"email": "sagonzal@microsoft.com",
"dependentChangeType": "none",
"date": "2020-08-20T19:21:58.954Z"
}
29 changes: 24 additions & 5 deletions lib/msal-common/src/cache/CacheManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,12 @@ export abstract class CacheManager implements ICacheManager {
};
const credentialCache: CredentialCache = this.getCredentialsFilteredBy(accessTokenFilter);
const accessTokens = Object.keys(credentialCache.accessTokens).map(key => credentialCache.accessTokens[key]);
if (accessTokens.length > 1) {
// TODO: Figure out what to throw or return here.
} else if (accessTokens.length < 1) {

const numAccessTokens = accessTokens.length;
if (numAccessTokens < 1) {
return null;
} else if (numAccessTokens > 1) {
throw ClientAuthError.createMultipleMatchingTokensInCacheError();
}

return accessTokens[0] as AccessTokenEntity;
Expand Down Expand Up @@ -322,7 +324,8 @@ export abstract class CacheManager implements ICacheManager {
filter.credentialType,
filter.clientId,
filter.realm,
filter.target
filter.target,
filter.oboAssertion,
);
}

Expand All @@ -341,7 +344,8 @@ export abstract class CacheManager implements ICacheManager {
credentialType?: string,
clientId?: string,
realm?: string,
target?: string
target?: string,
oboAssertion?: string
): CredentialCache {
const allCacheKeys = this.getKeys();
const matchingCredentials: CredentialCache = {
Expand All @@ -365,6 +369,10 @@ export abstract class CacheManager implements ICacheManager {
return;
}

if (!StringUtils.isEmpty(oboAssertion) && !this.matchOboAssertion(entity, oboAssertion)) {
return;
}

if (!StringUtils.isEmpty(homeAccountId) && !this.matchHomeAccountId(entity, homeAccountId)) {
return;
}
Expand Down Expand Up @@ -496,6 +504,17 @@ export abstract class CacheManager implements ICacheManager {
return entity.homeAccountId && homeAccountId === entity.homeAccountId;
}

/**
* @param value
* @param oboAssertion
*/
private matchOboAssertion(
entity: AccountEntity | CredentialEntity,
oboAssertion: string
): boolean {
return entity.oboAssertion && oboAssertion === entity.oboAssertion;
}

/**
*
* @param value
Expand Down
4 changes: 3 additions & 1 deletion lib/msal-common/src/cache/entities/AccessTokenEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export class AccessTokenEntity extends CredentialEntity {
tenantId: string,
scopes: string,
expiresOn: number,
extExpiresOn: number
extExpiresOn: number,
oboAssertion?: string
): AccessTokenEntity {
const atEntity: AccessTokenEntity = new AccessTokenEntity();

Expand All @@ -82,6 +83,7 @@ export class AccessTokenEntity extends CredentialEntity {
atEntity.clientId = clientId;
atEntity.realm = tenantId;
atEntity.target = scopes;
atEntity.oboAssertion = oboAssertion;

return atEntity;
}
Expand Down
14 changes: 10 additions & 4 deletions lib/msal-common/src/cache/entities/AccountEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import { ClientAuthError } from "../../error/ClientAuthError";
* name: Full name for the account, including given name and family name,
* clientInfo: Full base64 encoded client info received from ESTS
* lastModificationTime: last time this entity was modified in the cache
* lastModificationApp:
* lastModificationApp:
* oboAssertion: access token passed in as part of OBO request
* }
*/
export class AccountEntity {
Expand All @@ -49,6 +50,7 @@ export class AccountEntity {
clientInfo?: string;
lastModificationTime?: string;
lastModificationApp?: string;
oboAssertion?: string;

/**
* Generate Account Id key component as per the schema: <home_account_id>-<environment>
Expand Down Expand Up @@ -114,7 +116,7 @@ export class AccountEntity {

return accountKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase();
}

/**
* Build Account cache from IdToken, clientInfo and authority/policy
* @param clientInfo
Expand All @@ -126,7 +128,8 @@ export class AccountEntity {
clientInfo: string,
authority: Authority,
idToken: IdToken,
crypto: ICrypto
crypto: ICrypto,
oboAssertion?: string
): AccountEntity {
const account: AccountEntity = new AccountEntity();

Expand All @@ -143,6 +146,7 @@ export class AccountEntity {

account.environment = env;
account.realm = idToken.claims.tid;
account.oboAssertion = oboAssertion;

if (idToken) {
// How do you account for MSA CID here?
Expand All @@ -169,12 +173,14 @@ export class AccountEntity {
*/
static createADFSAccount(
authority: Authority,
idToken: IdToken
idToken: IdToken,
oboAssertion?: string
): AccountEntity {
const account: AccountEntity = new AccountEntity();

account.authorityType = CacheAccountType.ADFS_ACCOUNT_TYPE;
account.homeAccountId = idToken.claims.sub;
account.oboAssertion = oboAssertion;

const reqEnvironment = authority.canonicalAuthorityUrlComponents.HostNameAndPort;
const env = TrustedAuthority.getCloudDiscoveryMetadata(reqEnvironment) ? TrustedAuthority.getCloudDiscoveryMetadata(reqEnvironment).preferred_cache : "";
Expand Down
2 changes: 2 additions & 0 deletions lib/msal-common/src/cache/entities/CredentialEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ClientAuthError } from "../../error/ClientAuthError";
* familyId: Family ID identifier, usually only used for refresh tokens
* realm: Full tenant or organizational identifier that the account belongs to
* target: Permissions that are included in the token, or for refresh tokens, the resource identifier.
* oboAssertion: access token passed in as part of OBO request
* }
*/
export class CredentialEntity {
Expand All @@ -34,6 +35,7 @@ export class CredentialEntity {
familyId?: string;
realm?: string;
target?: string;
oboAssertion?: string;

/**
* Generate Account Id key component as per the schema: <home_account_id>-<environment>
Expand Down
4 changes: 3 additions & 1 deletion lib/msal-common/src/cache/entities/IdTokenEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export class IdTokenEntity extends CredentialEntity {
environment: string,
idToken: string,
clientId: string,
tenantId: string
tenantId: string,
oboAssertion?: string
): IdTokenEntity {
const idTokenEntity = new IdTokenEntity();

Expand All @@ -48,6 +49,7 @@ export class IdTokenEntity extends CredentialEntity {
idTokenEntity.clientId = clientId;
idTokenEntity.secret = idToken;
idTokenEntity.realm = tenantId;
idTokenEntity.oboAssertion = oboAssertion;

return idTokenEntity;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/msal-common/src/cache/entities/RefreshTokenEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export class RefreshTokenEntity extends CredentialEntity {
environment: string,
refreshToken: string,
clientId: string,
familyId?: string
familyId?: string,
oboAssertion?: string
): RefreshTokenEntity {
const rtEntity = new RefreshTokenEntity();

Expand All @@ -49,6 +50,7 @@ export class RefreshTokenEntity extends CredentialEntity {
rtEntity.environment = environment;
rtEntity.homeAccountId = homeAccountId;
rtEntity.secret = refreshToken;
rtEntity.oboAssertion = oboAssertion;

if (familyId)
rtEntity.familyId = familyId;
Expand Down
1 change: 1 addition & 0 deletions lib/msal-common/src/cache/utils/CacheTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ export type CredentialFilter = {
clientId?: string;
realm?: string;
target?: string;
oboAssertion?: string;
};
3 changes: 3 additions & 0 deletions lib/msal-common/src/client/ClientCredentialClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AccessTokenEntity } from "../cache/entities/AccessTokenEntity";
import { TimeUtils } from "../utils/TimeUtils";
import { StringUtils } from "../utils/StringUtils";
import { RequestThumbprint } from "../network/RequestThumbprint";
import { ClientAuthError } from "../error/ClientAuthError";

/**
* OAuth2.0 client credential grant
Expand Down Expand Up @@ -73,6 +74,8 @@ export class ClientCredentialClient extends BaseClient {
const accessTokens = Object.keys(credentialCache.accessTokens).map(key => credentialCache.accessTokens[key]);
if (accessTokens.length < 1) {
return null;
} else if (accessTokens.length > 1) {
throw ClientAuthError.createMultipleMatchingTokensInCacheError();
}
return accessTokens[0] as AccessTokenEntity;
}
Expand Down
Loading

0 comments on commit 126b83b

Please sign in to comment.