Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api sso silent #1166

Merged
merged 17 commits into from Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 33 additions & 9 deletions lib/msal-core/README.md
Expand Up @@ -2,11 +2,11 @@
Microsoft Authentication Library for JavaScript (MSAL.js)
=========================================================

| [Getting Started](https://docs.microsoft.com/en-us/azure/active-directory/develop/guidedsetups/active-directory-javascriptspa)| [AAD Docs](https://aka.ms/aaddevv2) | [Library Reference](https://azuread.github.io/microsoft-authentication-library-for-js/ref/msal-core/) | [Support](README.md#community-help-and-support) | [Samples](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/Samples)
| [Getting Started](https://docs.microsoft.com/azure/active-directory/develop/guidedsetups/active-directory-javascriptspa)| [AAD Docs](https://aka.ms/aaddevv2) | [Library Reference](https://azuread.github.io/microsoft-authentication-library-for-js/ref/msal-core/) | [Support](README.md#community-help-and-support) | [Samples](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/Samples)
| --- | --- | --- | --- | --- |


MSAL for JavaScript enables client-side JavaScript web applications, running in a web browser, to authenticate users using [Azure AD](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview) work and school accounts (AAD), Microsoft personal accounts (MSA) and social identity providers like Facebook, Google, LinkedIn, Microsoft accounts, etc. through [Azure AD B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-overview#identity-providers) service. It also enables your app to get tokens to access [Microsoft Cloud](https://www.microsoft.com/enterprise) services such as [Microsoft Graph](https://graph.microsoft.io).
MSAL for JavaScript enables client-side JavaScript web applications, running in a web browser, to authenticate users using [Azure AD](https://docs.microsoft.com/azure/active-directory/develop/v2-overview) work and school accounts (AAD), Microsoft personal accounts (MSA) and social identity providers like Facebook, Google, LinkedIn, Microsoft accounts, etc. through [Azure AD B2C](https://docs.microsoft.com/azure/active-directory-b2c/active-directory-b2c-overview#identity-providers) service. It also enables your app to get tokens to access [Microsoft Cloud](https://www.microsoft.com/enterprise) services such as [Microsoft Graph](https://graph.microsoft.io).

[![npm version](https://img.shields.io/npm/v/msal.svg?style=flat)](https://www.npmjs.com/package/msal)[![npm version](https://img.shields.io/npm/dm/msal.svg)](https://nodei.co/npm/msal/)[![Coverage Status](https://coveralls.io/repos/github/AzureAD/microsoft-authentication-library-for-js/badge.svg?branch=dev)](https://coveralls.io/github/AzureAD/microsoft-authentication-library-for-js?branch=dev)

Expand Down Expand Up @@ -67,7 +67,7 @@ Below is an example of how to use one CDN as a fallback when the other CDN is no
**Note:** This method of using `document.write` may be blocked in certain browsers in certain situations. More information can be found [here](https://www.chromestatus.com/feature/5718547946799104).

#### Notes:
- [Subresource Integrity (SRI)](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) attributes are optional in the script tag.
- [Subresource Integrity (SRI)](https://developer.mozilla.org/docs/Web/Security/Subresource_Integrity) attributes are optional in the script tag.
- All hashes are unique to the version of MSAL. You can find the previous hashes on the [MSAL Wiki page](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/MSAL-JS-CDN-URL-and-SRI-Hash).
- We recommend including the SRI Hash with all script tags when using `msal.js` or `msal.min.js` (including when using a third-party CDN). When providing the SRI Hash, you *must* also provide the `crossorigin="anonymous"` field in the same tag.

Expand All @@ -86,7 +86,8 @@ Our goal is to communicate extremely well with the community and to take their o
Please check our [roadmap](https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki#roadmap) to see what we are working on and what we are tracking next.

## OAuth 2.0 and the Implicit Flow
Msal implements the [Implicit Grant Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow), as defined by the OAuth 2.0 protocol and is [OpenID](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc) compliant.

Msal implements the [Implicit Grant Flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-implicit-grant-flow), as defined by the OAuth 2.0 protocol and is [OpenID](https://docs.microsoft.com/azure/active-directory/develop/v2-protocols-oidc) compliant.

Our goal is that the library abstracts enough of the protocol away so that you can get plug and play authentication, but it is important to know and understand the implicit flow from a security perspective.
The implicit flow runs in the context of a web browser which cannot manage client secrets securely. It is optimized for single page apps and has one less hop between client and server so tokens are returned directly to the browser. These aspects make it naturally less secure.
Expand All @@ -104,7 +105,7 @@ The example below walks you through how to login a user and acquire a token to b

#### Prerequisites

Before using MSAL.js you will need to [register an application in Azure AD](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) to get a valid `clientId` for configuration, and to register the routes that your app will accept redirect traffic on.
Before using MSAL.js you will need to [register an application in Azure AD](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) to get a valid `clientId` for configuration, and to register the routes that your app will accept redirect traffic on.

#### 1. Instantiate the UserAgentApplication

Expand All @@ -130,13 +131,13 @@ After instantiating your instance, if you plan on using a redirect flow in MSAL

```

For details on the configuration options, read [Initializing client applications with MSAL.js](https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications).
For details on the configuration options, read [Initializing client applications with MSAL.js](https://docs.microsoft.com/azure/active-directory/develop/msal-js-initializing-client-applications).

#### 2. Login the user

Your app must login the user with either the `loginPopup` or the `loginRedirect` method to establish user context.

When the login methods are called and the authentication of the user is completed by the Azure AD service, an [id token](https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens) is returned which is used to identify the user with some basic information.
When the login methods are called and the authentication of the user is completed by the Azure AD service, an [id token](https://docs.microsoft.com/azure/active-directory/develop/id-tokens) is returned which is used to identify the user with some basic information.

When you login a user, you can pass in scopes that the user can pre consent to on login, however this is not required. Please note that consenting to scopes on login, does not return an access_token for these scopes, but gives you the opportunity to obtain a token silently with these scopes passed in, with no further interaction from the user.

Expand All @@ -160,9 +161,32 @@ When the user makes a login request, you can pass in multiple resources and thei

```

##### ssoSilent

If you are confident that the user has an existing session and would like to establish user context without prompting for interaction, you can invoke `ssoSilent` with a `loginHint` or `sid` (available as an [optional claim](https://docs.microsoft.com/azure/active-directory/develop/active-directory-optional-claims)) and MSAL will attempt to silently SSO to the existing session and establish user context.

Note, if there is no active session for the given `loginHint` or `sid`, an error will be thrown, which should be handled by invoking an interactive login method (`loginPopup` or `loginRedirect`).

Example:

```js
const ssoRequest = {
loginHint: "user@example.com"
};

msalInstance.ssoSilent(ssoRequest)
.then(response => {
// session silently established
})
.catch(error => {
// handle error by invoking an interactive login method
msalInstance.loginPopup(ssoRequest);
});
```

#### 3. Get an access token to call an API

In MSAL, you can get access tokens for the APIs your app needs to call using the `acquireTokenSilent` method which makes a silent request (without prompting the user with UI) to Azure AD to obtain an access token. The Azure AD service then returns an [access token](https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens) containing the user consented scopes to allow your app to securely call the API.
In MSAL, you can get access tokens for the APIs your app needs to call using the `acquireTokenSilent` method which makes a silent request (without prompting the user with UI) to Azure AD to obtain an access token. The Azure AD service then returns an [access token](https://docs.microsoft.com/azure/active-directory/develop/access-tokens) containing the user consented scopes to allow your app to securely call the API.

You can use `acquireTokenRedirect` or `acquireTokenPopup` to initiate interactive requests, although, it is best practice to only show interactive experiences if you are unable to obtain a token silently due to interaction required errors. If you are using an interactive token call, it must match the login method used in your application. (`loginPopup`=> `acquireTokenPopup`, `loginRedirect` => `acquireTokenRedirect`).

Expand Down Expand Up @@ -223,7 +247,7 @@ You can learn further details about MSAL.js functionality documented in the [MSA

## Security Reporting

If you find a security issue with our libraries or services please report it to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://technet.microsoft.com/en-us/security/dd252948) and subscribing to Security Advisory Alerts.
If you find a security issue with our libraries or services please report it to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://technet.microsoft.com/security/dd252948) and subscribing to Security Advisory Alerts.

## License

Expand Down
22 changes: 22 additions & 0 deletions lib/msal-core/src/UserAgentApplication.ts
Expand Up @@ -595,6 +595,27 @@ export class UserAgentApplication {
});
}

/**
* API interfacing idToken request when applications already have a session/hint acquired by authorization client applications
* @param request
*/
ssoSilent(request: AuthenticationParameters): Promise<AuthResponse> {
jasonnutter marked this conversation as resolved.
Show resolved Hide resolved
// throw an error on an empty request
if (!request) {
throw ClientConfigurationError.createEmptyRequestError();
}

// throw an error on no hints passed
if (!request.sid && !request.loginHint) {
sameerag marked this conversation as resolved.
Show resolved Hide resolved
throw ClientConfigurationError.createSsoSilentError();
}

return this.acquireTokenSilent({
...request,
scopes: [this.clientId]
});
}

/**
* Use this function to obtain a token before every call to the API / resource provider
*
Expand Down Expand Up @@ -656,6 +677,7 @@ export class UserAgentApplication {
this.logger.verbose("ADAL's idToken exists. Extracting login information from ADAL's idToken ");
serverAuthenticationRequest.populateQueryParams(account, null, adalIdTokenObject, true);
}

const userContainedClaims = request.claimsRequest || serverAuthenticationRequest.claimsValue;

let authErr: AuthError;
Expand Down
2 changes: 1 addition & 1 deletion lib/msal-core/src/error/ClientAuthError.ts
Expand Up @@ -59,7 +59,7 @@ export const ClientAuthErrorMessage = {
},
userLoginRequiredError: {
code: "user_login_error",
desc: "User login is required."
desc: "User login is required. For silent calls, request must contain either sid of login_hint"
},
userDoesNotExistError: {
code: "user_non_existent",
Expand Down
9 changes: 9 additions & 0 deletions lib/msal-core/src/error/ClientConfigurationError.ts
Expand Up @@ -89,6 +89,10 @@ export const ClientConfigurationErrorMessage = {
telemetryConfigError: {
code: "telemetry_config_error",
desc: "Telemetry config is not configured with required values"
},
ssoSilentError: {
code: "sso_silent_error",
desc: "request must contain either sid of login_hint"
jasonnutter marked this conversation as resolved.
Show resolved Hide resolved
jasonnutter marked this conversation as resolved.
Show resolved Hide resolved
}
};

Expand Down Expand Up @@ -192,4 +196,9 @@ export class ClientConfigurationError extends ClientAuthError {

return new ClientConfigurationError(code, `${desc} mising values: ${missingKeys.join(",")}`);
}

static createSsoSilentError(): ClientConfigurationError {
return new ClientConfigurationError(ClientConfigurationErrorMessage.ssoSilentError.code,
ClientConfigurationErrorMessage.ssoSilentError.desc);
}
}
47 changes: 45 additions & 2 deletions lib/msal-core/test/UserAgentApplication.spec.ts
Expand Up @@ -1721,7 +1721,7 @@ describe("UserAgentApplication.ts Class", function () {
};
});
});

describe('Logger', () => {
it('getLogger and setLogger', done => {
const config: Configuration = {
Expand Down Expand Up @@ -1755,5 +1755,48 @@ describe("UserAgentApplication.ts Class", function () {

msal.getLogger().info('Message');
});
})
});

describe("ssoSilent", () => {
it("invokes acquireTokenSilent with loginHint", done => {
const loginHint = "test@example.com";

const atsStub = sinon.stub(msal, "acquireTokenSilent").callsFake(async (request) => {
expect(request.loginHint).to.equal(loginHint);
expect(request.scopes).to.deep.equal([ msal.getCurrentConfiguration().auth.clientId ]);

atsStub.restore();
done();
});

msal.ssoSilent({
loginHint
});
});

it("invokes acquireTokenSilent with sid", done => {
const sid = "fakesid";

const atsStub = sinon.stub(msal, "acquireTokenSilent").callsFake(async (request) => {
expect(request.sid).to.equal(sid);
expect(request.scopes).to.deep.equal([ msal.getCurrentConfiguration().auth.clientId ]);

atsStub.restore();
done();
});

msal.ssoSilent({
sid
});
});

it("throws if sid or login isnt provided", done => {
try {
msal.ssoSilent({});
} catch (e) {
expect(e.errorCode).to.equal("sso_silent_error");
done();
}
});
});
});