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

Adding kiota msal-browser auth library #233

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/authentication/msal-browser/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.js
*.js.map
*.d.ts

node_modules
lib
spec/development
237 changes: 237 additions & 0 deletions packages/authentication/msal-browser/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"prettier",
"simple-import-sort"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/no-empty-interface": "warn",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeLike",
"format": [
"PascalCase"
],
"filter": {
"regex": "^(__String|[A-Za-z]+_[A-Za-z]+)$",
"match": false
}
},
{
"selector": "interface",
"format": [
"PascalCase"
],
"custom": {
"regex": "^I[A-Z]",
"match": false
},
"filter": {
"regex": "^I(Arguments|TextWriter|O([A-Z][a-z]+[A-Za-z]*)?)$",
"match": false
}
},
{
"selector": "variable",
"format": [
"camelCase",
"PascalCase",
"UPPER_CASE"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^(_{1,2}filename|_{1,2}dirname|_+|[A-Za-z]+_[A-Za-z]+)$",
"match": false
}
},
{
"selector": "function",
"format": [
"camelCase",
"PascalCase"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^[A-Za-z]+_[A-Za-z]+$",
"match": false
}
},
{
"selector": "parameter",
"format": [
"camelCase"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^(_+|[A-Za-z]+_[A-Z][a-z]+)$",
"match": false
}
},
{
"selector": "method",
"format": [
"camelCase",
"PascalCase"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^[A-Za-z]+_[A-Za-z]+$",
"match": false
}
},
{
"selector": "memberLike",
"format": [
"camelCase"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^[A-Za-z]+_[A-Za-z]+$",
"match": false
}
},
{
"selector": "enumMember",
"format": [
"camelCase",
"PascalCase"
],
"leadingUnderscore": "allow",
"filter": {
"regex": "^[A-Za-z]+_[A-Za-z]+$",
"match": false
}
},
{
"selector": "property",
"format": null
}
],
"@typescript-eslint/semi": "error",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-namespace-keyword": "error",
"@typescript-eslint/quotes": [
"error",
"double",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"@typescript-eslint/space-within-parens": [
"off",
"never"
],
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unified-signatures": "error",
"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/consistent-type-definitions": [
"error",
"interface"
],
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-this-alias": "error",
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": [
"error",
{
"allowTernary": true
}
],
"@typescript-eslint/space-before-function-paren": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"@typescript-eslint/indent": "off",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/member-delimiter-style": [
"off",
{
"multiline": {
"delimiter": "none",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-array-constructor": "error",
"no-useless-catch": "error",
"prefer-rest-params": "off",
"no-constant-condition": "error",
"simple-import-sort/imports": "error",
"brace-style": "error",
"constructor-super": "error",
"curly": [
"error",
"multi-line"
],
"dot-notation": "off",
"eqeqeq": "error",
"new-parens": "error",
"no-caller": "error",
"no-duplicate-case": "error",
"no-duplicate-imports": "error",
"no-empty": "error",
"no-eval": "error",
"no-extra-bind": "error",
"no-fallthrough": "error",
"no-new-func": "off",
"no-new-wrappers": "error",
"no-return-await": "off",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef-init": "error",
"no-unsafe-finally": "error",
"no-unused-labels": "error",
"no-var": "error",
"object-shorthand": "error",
"prefer-const": "error",
"prefer-object-spread": "error",
"quote-props": "off",
"space-in-parens": "error",
"unicode-bom": [
"error",
"never"
],
"use-isnan": "error"
}
}
2 changes: 2 additions & 0 deletions packages/authentication/msal-browser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
*.tsbuildinfo
45 changes: 45 additions & 0 deletions packages/authentication/msal-browser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@microsoft/kiota-authentication-msal-browser",
"version": "1.0.0-preview.1",
"description": "Authentication provider for Kiota using MSAL browser",
"main": "dist/cjs/src/index.js",
"module": "dist/es/src/index.js",
"types": "dist/cjs/src/index.d.ts",
"files": [
"src",
"dist"
],
"scripts": {
"build": "npm run build:cjs && npm run build:esm",
"build:cjs": "tsc -b tsconfig.cjs.json",
"build:esm": "tsc -b tsconfig.es.json",
"test:cjs": "npm run build:cjs && mocha 'dist/cjs/test/**/*.js'",
"test:es": " npm run build:esm && mocha 'dist/es/test/**/*.js' --require esm",
"test": "npm run test:cjs && npm run test:es",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"clean": "rm -r ./dist"
},
"repository": "git://github.com/microsoft/kiota-typescript.git",
"keywords": [
"Kiota",
"OpenApi",
"MSAL",
"Authentication",
"Browser"
],
"author": "Microsoft",
"license": "MIT",
"bugs": {
"url": "https://github.com/microsoft/kiota-typescript/issues"
},
"homepage": "https://github.com/microsoft/kiota-typescript#readme",
"dependencies": {
"@azure/msal-browser": "^2.26.0",
"@microsoft/kiota-abstractions": "1.0.0-preview.6",
"tslib": "^2.3.1"
},
"devDependencies": {
"@types/mocha": "^9.1.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
AuthenticationResult,
InteractionRequiredAuthError,
InteractionType,
} from "@azure/msal-browser";
import {
AccessTokenProvider,
AllowedHostsValidator,
validateProtocol,
} from "@microsoft/kiota-abstractions";

import { MSALBrowserAuthenticationConfig } from "./msalBrowserAuthenticationConfig";

/** Access token provider that leverages the MSALBrowser Identity library to retrieve an access token. */
export class MSALBrowserAccessTokenProvder implements AccessTokenProvider {
/**
*@param msalBrowserAuthenticationConfig The MSALBrowser public client application, scopes, account type and interaction type to use for authentication.
*@param allowedHosts The allowed hosts to use for authentication.
*/
public constructor(
private msalBrowserAuthenticationConfig: MSALBrowserAuthenticationConfig,
allowedHosts: Set<string>
) {
if (!msalBrowserAuthenticationConfig) {
throw new Error(
"Please pass valid PublicClientApplication instance, AccountInfo, Scopes and InteractionType"
);
}
this.allowedHostsValidator = new AllowedHostsValidator(allowedHosts);
}
private readonly allowedHostsValidator: AllowedHostsValidator;

/**
* @inheritdoc
*/
public async getAuthorizationToken(url?: string): Promise<string> {
if (!url || !this.allowedHostsValidator.isUrlHostValid(url)) {
return "";
}
validateProtocol(url);

const scopes =
this.msalBrowserAuthenticationConfig &&
this.msalBrowserAuthenticationConfig.scopes;
const account =
this.msalBrowserAuthenticationConfig &&
this.msalBrowserAuthenticationConfig.account;
const error = new Error();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue for duplication and instantiating it only if needed. Hopefully the vast majority of executions do not throw an error.

if (!scopes || scopes.length === 0) {
error.name = "Empty Scopes";
error.message = "Scopes cannot be empty, Please provide scopes";
throw error;
}
try {
const response: AuthenticationResult =
await this.msalBrowserAuthenticationConfig.publicClientApplication.acquireTokenSilent(
{
scopes,
account,
}
);
if (!response || !response.accessToken) {
error.name = "Access token is undefined";
error.message =
"Received empty access token from PublicClientApplication";
throw error;
}
return response.accessToken;
} catch (error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this exception name masks the error variable your have above. Rename this or downscope the other one

if (error instanceof InteractionRequiredAuthError) {
if (
this.msalBrowserAuthenticationConfig.interactionType ===
InteractionType.Redirect
) {
this.msalBrowserAuthenticationConfig.publicClientApplication.acquireTokenRedirect(
{ scopes }
);
} else if (
this.msalBrowserAuthenticationConfig.interactionType ===
InteractionType.Popup
) {
const response: AuthenticationResult =
await this.msalBrowserAuthenticationConfig.publicClientApplication.acquireTokenPopup(
{ scopes }
);
return response.accessToken;
}
}
throw error;
}
}
/**
* @inheritdoc
*/
public getAllowedHostsValidator = () => this.allowedHostsValidator;
}
Loading