Skip to content

Commit

Permalink
Feature: Keycloak Angular v19 Release (#599)
Browse files Browse the repository at this point in the history
* feat: Keycloak Angular v19 release

- Update to Angular v19
- NgModules are deprecated, to use the functional approach instead.
- New functionalities such as provideKeycloak, directives, interceptors, signal, guards.md, keycloak angular features.
- Refactored and added new examples.

* feat: CR fixes.

- Fix typo in NgModule documentation.
- Add inject instead of adding in the constructor.

* feat: improved documentation and examples

- Add migration guide for v19
- Improved README.md
- Migrated examples to use inject()

* feat: updated the remaining documentation

* feat: updated README.md to contain full path.

- Changed contact to bluesky in package.json.
  • Loading branch information
mauriciovigolo authored Dec 23, 2024
1 parent 9a002d5 commit 9b521cf
Show file tree
Hide file tree
Showing 109 changed files with 11,728 additions and 6,861 deletions.
5 changes: 1 addition & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
"project": ["tsconfig.json", "e2e/tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"extends": ["plugin:@angular-eslint/recommended", "plugin:@angular-eslint/template/process-inline-templates"],
"rules": {
"@angular-eslint/component-selector": [
"error",
Expand Down
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 80,
"printWidth": 120,
"singleQuote": true,
"bracketSpacing": true,
"semi": true,
Expand Down
10 changes: 4 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,22 @@ git clone https://github.com/YOUR-USERNAME/keycloak-angular.git
npm install
```

- Now, lets code! :smile:

## <a name="sug"></a> Submission guidelines

### For issues

Before submiting an issue, please search if there is an open issue for the same bug. A minimal reproduce scenario is necessary to help to understand the problem.
Before submitting an issue, please search if there is an open issue for the same bug. A minimal reproduce scenario is necessary to help to understand the problem.

For opening an issue, you can fill out this [issue form](https://github.com/mauriciovigolo/keycloak-angular/issues/new).

### Submitting a Pull Request (PR)

Before submiting a Pull Request, please:
Before submitting a Pull Request, please:

- Search for open and closed related PRs.
- Follow the coding rules.
- Follow the commit message guidelines.
- Checkout the code documentation - jsdocs.
- Checkout the code documentation - jsdoc.

## <a name="cru"></a> Coding rules

Expand Down Expand Up @@ -126,6 +124,6 @@ Breaking Changes should start with the word BREAKING CHANGE: with a space or two

## <a name="fmg"></a> Final message

Thanks for your interest in this project. Hope to see your contribution!
Thank you for your interest in this project. Hope to see your contribution!

See you and happy coding!
394 changes: 244 additions & 150 deletions README.md

Large diffs are not rendered by default.

135 changes: 105 additions & 30 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"root": "projects/keycloak-angular",
"sourceRoot": "projects/keycloak-angular/src",
"projectType": "library",
"prefix": "lib",
"prefix": "ka",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
Expand All @@ -25,46 +25,40 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/keycloak-angular/src/test.ts",
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "projects/keycloak-angular/tsconfig.spec.json",
"karmaConfig": "projects/keycloak-angular/karma.conf.js"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"projects/keycloak-angular/**/*.ts",
"projects/keycloak-angular/**/*.html"
]
"lintFilePatterns": ["projects/keycloak-angular/**/*.ts", "projects/keycloak-angular/**/*.html"]
}
}
}
},
"example": {
"example-ngmodule": {
"projectType": "application",
"schematics": {},
"root": "projects/example",
"sourceRoot": "projects/example/src",
"root": "projects/examples/ngmodule",
"sourceRoot": "projects/examples/ngmodule/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist/example"
"base": "dist/examples/ngmodule"
},
"index": "projects/example/src/index.html",
"index": "projects/examples/ngmodule/src/index.html",
"polyfills": ["zone.js"],
"tsConfig": "projects/example/tsconfig.app.json",
"assets": [
"projects/example/src/favicon.ico",
"projects/example/src/assets"
],
"tsConfig": "projects/examples/ngmodule/tsconfig.app.json",
"assets": ["projects/examples/ngmodule/src/favicon.ico", "projects/examples/ngmodule/src/assets"],
"allowedCommonJsDependencies": ["base64-js", "js-sha256"],
"styles": ["projects/example/src/styles.css"],
"styles": ["projects/examples/ngmodule/src/styles.css"],
"scripts": [],
"browser": "projects/example/src/main.ts"
"browser": "projects/examples/ngmodule/src/main.ts"
},
"configurations": {
"production": {
Expand Down Expand Up @@ -95,46 +89,127 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "example:build:production"
"buildTarget": "example-ngmodule:build:production"
},
"development": {
"buildTarget": "example:build:development"
"buildTarget": "example-ngmodule:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "example:build"
"buildTarget": "example-ngmodule:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "projects/examples/ngmodule/tsconfig.spec.json",
"assets": ["projects/examples/ngmodule/src/favicon.ico", "projects/examples/ngmodule/src/assets"],
"styles": ["projects/examples/ngmodule/src/styles.css"],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["projects/examples/ngmodule/**/*.ts", "projects/examples/ngmodule/**/*.html"]
}
}
}
},
"example-standalone": {
"projectType": "application",
"schematics": {},
"root": "projects/examples/standalone",
"sourceRoot": "projects/examples/standalone/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/examples/standalone",
"index": "projects/examples/standalone/src/index.html",
"browser": "projects/examples/standalone/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "projects/examples/standalone/tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "projects/examples/standalone/public"
}
],
"styles": ["projects/examples/standalone/src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "example-standalone:build:production"
},
"development": {
"buildTarget": "example-standalone:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "projects/example/tsconfig.spec.json",
"tsConfig": "projects/examples/standalone/tsconfig.spec.json",
"assets": [
"projects/example/src/favicon.ico",
"projects/example/src/assets"
{
"glob": "**/*",
"input": "projects/examples/standalone/public"
}
],
"styles": ["projects/example/src/styles.css"],
"styles": ["projects/examples/standalone/src/styles.css"],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"projects/example/**/*.ts",
"projects/example/**/*.html"
]
"lintFilePatterns": ["projects/examples/standalone/**/*.ts", "projects/examples/standalone/**/*.html"]
}
}
}
}
},
"cli": {
"schematicCollections": ["@angular-eslint/schematics"]
"schematicCollections": ["@angular-eslint/schematics"],
"analytics": false
}
}
92 changes: 92 additions & 0 deletions docs/directives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Keycloak-Angular Directives

Keycloak-Angular provides directives to manage role-based rendering and access control in Angular templates. These directives are designed to simplify the process of showing or hiding parts of the UI based on the authenticated user's roles and access permissions.

---

## **1. `*kaHasRoles` Directive**

### Purpose

The `*kaHasRoles` directive is a structural directive that conditionally renders DOM elements based on the authenticated user's roles. It supports validation against both realm roles and resource roles.

### Key Features

- **Role-Based Rendering**: Displays or hides DOM elements based on user roles.
- **Resource Role Validation**: Supports client-specific resource roles.
- **Realm Role Validation**: Allows checking roles assigned at the realm level.
- **Fallback Content**: Supports fallback templates when the user lacks required roles.

---

### Inputs

| Input | Type | Description |
| ---------------------- | ---------- | ---------------------------------------------------------------------------- |
| `kaHasRoles` | `string[]` | Array of roles to validate against. |
| `kaHasRolesResource` | `string` | (Optional) The resource (client ID) to validate roles against. |
| `kaHasRolesCheckRealm` | `boolean` | (Optional) Whether to validate roles at the realm level. Default is `false`. |

---

### Requirements

1. A Keycloak instance must be properly configured and injected via Angular's dependency injection.
2. The user must be authenticated in Keycloak for role checks to work.

---

### Examples

#### Example 1: Checking Realm Roles

Display content only if the user has the `admin` or `editor` role at the realm level.

```html
<div *kaHasRoles="['admin', 'editor']; kaHasRolesCheckRealm: true">
<p>This content is visible to users with 'admin' or 'editor' realm roles.</p>
</div>
```

#### Example 2: Checking Resource Roles

Display content only if the user has the `read` or `write` role for the `my-client` resource.

```html
<div *kaHasRoles="['read', 'write']; kaHasRolesResource: 'my-client'">
<p>This content is visible to users with 'read' or 'write' roles for 'my-client'.</p>
</div>
```

#### Example 3: Fallback Content

Provide fallback content for users without the required roles.

```html
<div *kaHasRoles="['admin']; kaHasRolesResource: 'my-client'">
<p>Welcome, Admin!</p>
</div>
<ng-template #noAccess>
<p>Access Denied</p>
</ng-template>
```

#### Example 4: Combining Realm and Resource Role Checks

Display content if the user has the roles in either the realm or a specific resource.

```typescript
<div *kaHasRoles="['admin', 'write']; kaHasRolesResource: 'my-client'; kaHasRolesCheckRealm: true">
<p>This content is visible to users with 'admin' in the realm or 'write' in 'my-client'.</p>
</div>
```

#### Implementation Details

The directive uses Keycloak's role-checking APIs (`hasRealmRole` and `hasResourceRole`) to validate roles. If the user matches any of the specified roles, the content is rendered; otherwise, it is removed from the DOM.

### Usage Notes

- If no resource is specified using `kaHasRolesResource`, the directive defaults to the Keycloak application client ID.
- Combine realm and resource checks for advanced role-based access scenarios.
- Ensure the roles specified in the directive exist in your Keycloak server configuration.
Loading

0 comments on commit 9b521cf

Please sign in to comment.