Skip to content

[CustomAuth] Angular Sign In completed #7635

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

Open
wants to merge 1 commit into
base: custom-auth/main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions lib/msal-custom-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
},
"description": "Microsoft Authentication Library for Native Authentication",
"type": "module",
"module": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"main": "lib/msal-custom-auth.cjs",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
"default": "./dist/index.mjs"
},
"require": {
"types": "./lib/types/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class SignInApiClient extends BaseApiClient {
correlationId: string,
): Promise<SignInTokenResponse> {
// The client_info parameter is required for MSAL to return the uid and utid in the response.
requestData.client_info = true;
requestData["client_info"] = true;

const result = await this.request<SignInTokenResponse>(
CustomAuthApiEndpoint.SIGNIN_TOKEN,
Expand Down
2 changes: 2 additions & 0 deletions lib/msal-custom-auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,5 @@ export { UserAlreadySignedInError } from "./core/error/UserAlreadySignedInError.

// Components from msal_browser
export { LogLevel } from "@azure/msal-browser";

export { SignInState } from "./core/auth_flow/AuthFlowStateBase.js";
23 changes: 13 additions & 10 deletions samples/msal-custom-auth-samples/angular-sample/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Editor configuration, see https://editorconfig.org
root = true

# Editor configuration, see http://editorconfig.org
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120

[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.json]
indent_size = 2

[*.md]
max_line_length = off
max_line_length = 140
trim_trailing_whitespace = false

[*.html]
max_line_length = 200
trim_trailing_whitespace = false
149 changes: 125 additions & 24 deletions samples/msal-custom-auth-samples/angular-sample/README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,160 @@
# AngularSample
# MSAL Custom Auth Angular Sample

This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.1.
This sample demonstrates how to implement custom authentication flows in an Angular application using the Microsoft Authentication Library (MSAL) for JavaScript with custom authentication.

## Development server
## Overview

To start a local development server, run:
This sample showcases a complete authentication flow with username/password sign-in and OTP (One-Time Password) challenge handling. It demonstrates how to:

```bash
ng serve
1. Implement a sign-in form with username and password
2. Handle OTP challenges when required by the authentication service
3. Manage authentication state using Angular services
4. Use Angular standalone components and reactive forms

## Project Structure

```
angular-sample/
├── src/
│ ├── app/
│ │ ├── components/
│ │ │ ├── sign-in/ # Sign-in component with username/password form
│ │ │ │ ├── sign-in.component.ts
│ │ │ │ ├── sign-in.component.html
│ │ │ │ └── sign-in.component.scss
│ │ │ └── otp/ # OTP component for handling verification codes
│ │ │ ├── otp.component.ts
│ │ │ ├── otp.component.html
│ │ │ └── otp.component.scss
│ │ ├── services/
│ │ │ └── auth.service.ts # Authentication service for MSAL integration
│ │ ├── models/
│ │ │ └── auth.models.ts # TypeScript interfaces for auth-related data
│ │ ├── app.component.ts # Root component
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.config.ts # App configuration
│ │ └── app.routes.ts # Angular routing configuration
│ ├── index.html
│ ├── main.ts
│ └── styles.scss
├── angular.json # Angular CLI configuration
├── package.json # Project dependencies
└── tsconfig.json # TypeScript configuration
```

Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Prerequisites

- Node.js and npm
- Angular CLI
- A Microsoft Entra ID tenant with custom authentication enabled
- Client ID and authority URL for your application

## Code scaffolding
## Setup

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
1. Clone the repository:

```bash
ng generate component component-name
git clone https://github.com/AzureAD/microsoft-authentication-library-for-js.git
cd microsoft-authentication-library-for-js/samples/msal-custom-auth-samples/angular-sample
```

For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
2. Install dependencies:

```bash
ng generate --help
npm install
```

## Building
3. Configure the authentication settings:

Open `src/app/services/auth.service.ts` and update the MSAL configuration with your application's details:

```typescript
const msalConfig: CustomAuthConfiguration = {
auth: {
clientId: "your-client-id", // Replace with your client ID
authority: "https://your-tenant.ciamlogin.com", // Replace with your CIAM authority
redirectUri: window.location.origin,
},
cache: {
cacheLocation: "localStorage",
},
customAuth: {
// Add any custom auth configuration here
},
};
```

To build the project run:
4. Start the development server:

```bash
ng build
npm start
```

This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
5. Navigate to `http://localhost:4200` in your browser.

## Authentication Flow

This sample implements the following authentication flow:

1. User enters username and password in the sign-in form
2. The application sends the credentials to the authentication service
3. If the service requires additional verification, an OTP challenge is presented
4. User enters the verification code
5. Upon successful authentication, the user is redirected to the home page

## Key Components

## Running unit tests
### AuthService

To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
The `AuthService` manages the authentication state and provides methods for:

- Initializing the MSAL instance
- Handling sign-in with username and password
- Processing OTP verification
- Managing authentication state
- Handling sign-out

### SignInComponent

The `SignInComponent` provides a user interface for:

- Collecting username and password
- Displaying validation errors
- Showing loading states during authentication
- Conditionally displaying the OTP component when required

### OtpComponent

The `OtpComponent` handles:

- Collecting the verification code
- Validating the code format
- Submitting the code to complete authentication
- Displaying error messages

## Development server

To start a local development server, run:

```bash
ng test
ng serve
```

## Running end-to-end tests
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.

For end-to-end (e2e) testing, run:
## Building

To build the project run:

```bash
ng e2e
ng build
```

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.

## Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
- [Microsoft Authentication Library (MSAL) for JavaScript](https://github.com/AzureAD/microsoft-authentication-library-for-js)
- [Angular Documentation](https://angular.dev)
- [Microsoft Entra ID Documentation](https://learn.microsoft.com/en-us/entra/identity/)
73 changes: 73 additions & 0 deletions samples/msal-custom-auth-samples/angular-sample/cors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const http = require("http");
const https = require("https");
const url = require("url");
const proxyConfig = require("./proxy.config");

const extraHeaders = [
"x-client-SKU",
"x-client-VER",
"x-client-OS",
"x-client-CPU",
"x-client-current-telemetry",
"x-client-last-telemetry",
"client-request-id",
];
http.createServer((req, res) => {
const reqUrl = url.parse(req.url);
const domain = url.parse(proxyConfig.proxy).hostname;

// Set CORS headers for all responses including OPTIONS
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, " + extraHeaders.join(", "),
"Access-Control-Allow-Credentials": "true",
"Access-Control-Max-Age": "86400", // 24 hours
};

// Handle preflight OPTIONS request
if (req.method === "OPTIONS") {
res.writeHead(204, corsHeaders);
res.end();
return;
}

if (reqUrl.pathname.startsWith(proxyConfig.localApiPath)) {
const targetUrl = proxyConfig.proxy + reqUrl.pathname?.replace(proxyConfig.localApiPath, "") + (reqUrl.search || "");

console.log("Incoming request -> " + req.url + " ===> " + reqUrl.pathname);

const proxyReq = https.request(
targetUrl,
{
method: req.method,
headers: {
...req.headers,
host: domain,
},
},
(proxyRes) => {
res.writeHead(proxyRes.statusCode, {
...proxyRes.headers,
...corsHeaders,
});

proxyRes.pipe(res);
}
);

proxyReq.on("error", (err) => {
console.error("Error with the proxy request:", err);
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Proxy error.");
});

req.pipe(proxyReq);
} else {
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Not Found");
}
}).listen(proxyConfig.port, () => {
console.log("CORS proxy running on http://localhost:3001");
console.log("Proxying from " + proxyConfig.localApiPath + " ===> " + proxyConfig.proxy);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
"test": "ng test",
"cors": "node cors.js"
},
"private": true,
"dependencies": {
"@azure/msal-custom-auth": "^0.0.1",
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
Expand Down
12 changes: 12 additions & 0 deletions samples/msal-custom-auth-samples/angular-sample/proxy.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Proxy configuration for local development
* entryPath: The path to the API on the react app ex. /api
* proxy: The URL to proxy the requests
*/
const config = {
localApiPath: "/api",
port: 3001,
proxy: "https://spasamples.ciamlogin.com/1eb974cd-0dc5-40a6-9f68-94b19f5535c5",
};

module.exports = config;
Loading