Skip to content

Commit

Permalink
Trharris/authenticate https (#2270)
Browse files Browse the repository at this point in the history
* Restrict authentication.authenticate to only accept https urls

* Create @microsoft-teams-js-40968a44-8220-4162-a289-038e99f5bee3.json

* Updated based on PR feedback

* Updated to use new validation functions and added(?) support for URL encoded strings

* abstract out the auth code's use of the a tag and then add tests to make sure URL encoded partial urls work correctly

* Re-order imports

* Updated based on PR feedback

---------

Co-authored-by: AE ( ͡ಠ ʖ̯ ͡ಠ) <36546967+AE-MS@users.noreply.github.com>
  • Loading branch information
2 people authored and alexneyman-MSFT committed May 6, 2024
1 parent 576d54b commit 68fcfc9
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 162 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Updated `authentication.authenticate` so that it only accepts https URLs.",
"packageName": "@microsoft/teams-js",
"email": "trharris@microsoft.com",
"dependentChangeType": "patch"
}
24 changes: 24 additions & 0 deletions packages/teams-js/src/internal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,30 @@ export function validateUrl(url: URL, errorToThrow?: Error): void {
}
}

/**
* This function takes in a string that represents a full or relative path and returns a
* fully qualified URL object.
*
* Currently this is accomplished by assigning the input string to an a tag and then retrieving
* the a tag's href value. A side effect of doing this is that the string becomes a fully qualified
* URL. This is probably not how I would choose to do this, but in order to not unintentionally
* break something I've preseved the functionality here and just isolated the code to make it
* easier to mock.
*
* @example
* `fullyQualifyUrlString('https://example.com')` returns `new URL('https://example.com')`
* `fullyQualifyUrlString('helloWorld')` returns `new URL('https://example.com/helloWorld')`
* `fullyQualifyUrlString('hello%20World')` returns `new URL('https://example.com/hello%20World')`
*
* @param fullOrRelativePath A string representing a full or relative URL.
* @returns A fully qualified URL representing the input string.
*/
export function fullyQualifyUrlString(fullOrRelativePath: string): URL {
const link = document.createElement('a');
link.href = fullOrRelativePath;
return new URL(link.href);
}

/**
* The hasScriptTags function first decodes any HTML entities in the input string using the decodeHTMLEntities function.
* It then tries to decode the result as a URI component. If the URI decoding fails (which would throw an error), it assumes that the input was not encoded and uses the original input.
Expand Down
30 changes: 11 additions & 19 deletions packages/teams-js/src/public/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { GlobalVars } from '../internal/globalVars';
import { registerHandler, removeHandler } from '../internal/handlers';
import { ensureInitializeCalled, ensureInitialized } from '../internal/internalAPIs';
import { ApiName, ApiVersionNumber, getApiVersionTag } from '../internal/telemetry';
import { fullyQualifyUrlString, validateUrl } from '../internal/utils';
import { FrameContexts, HostClientType } from './constants';
import { runtime } from './runtime';

Expand Down Expand Up @@ -160,26 +161,15 @@ export namespace authentication {

function authenticateHelper(apiVersionTag: string, authenticateParameters: AuthenticateParameters): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (
GlobalVars.hostClientType === HostClientType.desktop ||
GlobalVars.hostClientType === HostClientType.android ||
GlobalVars.hostClientType === HostClientType.ios ||
GlobalVars.hostClientType === HostClientType.ipados ||
GlobalVars.hostClientType === HostClientType.macos ||
GlobalVars.hostClientType === HostClientType.rigel ||
GlobalVars.hostClientType === HostClientType.teamsRoomsWindows ||
GlobalVars.hostClientType === HostClientType.teamsRoomsAndroid ||
GlobalVars.hostClientType === HostClientType.teamsPhones ||
GlobalVars.hostClientType === HostClientType.teamsDisplays ||
GlobalVars.hostClientType === HostClientType.surfaceHub
) {
if (GlobalVars.hostClientType !== HostClientType.web) {
// Convert any relative URLs into absolute URLs before sending them over to the parent window.
const link = document.createElement('a');
link.href = authenticateParameters.url;
const fullyQualifiedURL: URL = fullyQualifyUrlString(authenticateParameters.url);
validateUrl(fullyQualifiedURL);

// Ask the parent window to open an authentication window with the parameters provided by the caller.
resolve(
sendMessageToParentAsync<[boolean, string]>(apiVersionTag, 'authentication.authenticate', [
link.href,
fullyQualifiedURL.href,
authenticateParameters.width,
authenticateParameters.height,
authenticateParameters.isExternal,
Expand Down Expand Up @@ -358,9 +348,11 @@ export namespace authentication {
// Ensure that the new window is always smaller than our app's window so that it never fully covers up our app
width = Math.min(width, Communication.currentWindow.outerWidth - 400);
height = Math.min(height, Communication.currentWindow.outerHeight - 200);

// Convert any relative URLs into absolute URLs before sending them over to the parent window
const link = document.createElement('a');
link.href = authenticateParameters.url.replace('{oauthRedirectMethod}', 'web');
const fullyQualifiedURL = fullyQualifyUrlString(authenticateParameters.url.replace('{oauthRedirectMethod}', 'web'));
validateUrl(fullyQualifiedURL);

// We are running in the browser, so we need to center the new window ourselves
let left: number =
typeof Communication.currentWindow.screenLeft !== 'undefined'
Expand All @@ -374,7 +366,7 @@ export namespace authentication {
top += Communication.currentWindow.outerHeight / 2 - height / 2;
// Open a child window with a desired set of standard browser features
Communication.childWindow = Communication.currentWindow.open(
link.href,
fullyQualifiedURL.href,
'_blank',
'toolbar=no, location=yes, status=no, menubar=no, scrollbars=yes, top=' +
top +
Expand Down

0 comments on commit 68fcfc9

Please sign in to comment.