ngx-auth-client is a reactive authentication layer for Angular, published on npm as @ismailza/ngx-auth-client. It exposes auth state as signals, protects routes with a functional guard, and keeps feature code free of any identity-provider import.
bootstrapApplication(App, {
providers: [provideAuth(withKeycloak({ url, realm, clientId }))],
});@Component({
template: `@if (auth.authenticated()) {
Hello {{ auth.claims()?.name }}
}`,
})
export class Header {
protected readonly auth = inject(AuthService);
}It ships two adapters — one for Keycloak, and one for a backend you run yourself — and the component code above is identical with either. That is the whole integration surface for a component: inject one service, read signals. Everything else — token refresh, role mapping, redirects, which requests get a bearer token — is configuration, decided once at bootstrap.
Why this exists
Angular is signals-first and zoneless by default. Reading authentication state should look like reading any other piece of state in the application.
Today it doesn't. keycloak-angular exposes a signal for events (KEYCLOAK_EVENT_SIGNAL), but authentication state still lives on the Keycloak instance as plain getters — keycloak.authenticated, keycloak.token, keycloak.tokenParsed. Deriving reactive state from that is left to the application: subscribe to the event signal, decide which events matter, and maintain your own signals from them. Every project I've touched ends up writing that same layer, slightly differently, with slightly different bugs.
In a zoneless application the getters aren't merely inconvenient, they're invisible:
@if (keycloak.authenticated) { ... } // ✗ never updates — nothing schedules CDZone.js used to paper over this by re-rendering after every async task. Without it, getter-based state simply doesn't participate in change detection. ngx-auth-client writes that derivation layer once:
@if (auth.authenticated()) { ... } // ✓ updates on login, logout, refreshThe second reason is coupling. Auth touches routing, HTTP, and templates — the parts of a codebase most expensive to change later. Putting a provider-agnostic port at that boundary means swapping or upgrading the identity provider stays a one-file change instead of a rewrite.
When something else is the better call: angular-auth-oidc-client if you need certified, generic OIDC across arbitrary providers — it's OpenID-certified and mature. keycloak-angular directly if you're happy deriving state from events yourself. First-party SDKs if you're on Auth0 or Entra ID today — adapters for those are on the roadmap here, but shipped beats planned.
What it gives you
- Auth state as signals —
authenticated,claims, androlesupdate on login, logout, and token refresh with no manual event wiring - A functional authGuard — role requirements set via
allOf/anyOfon route data, accumulating down the route tree - An allowlisted authTokenInterceptor — attaches bearer tokens only to requests matching a configured URL pattern, and fails closed on misconfiguration
- A Keycloak adapter (
withKeycloak) built on a provider-agnosticAuthProviderport - An HTTP adapter (
withHttpAuth) for home-made authentication — your own login form, a JWT access token in memory, a refresh token in anhttpOnlycookie, and no extra dependency - A fake adapter (
withFakeAuth) for testing without a real server - Tokens in memory only — nothing written to
localStorageorsessionStorage - PKCE (S256) on by default, and not silently disableable
- SSR safety — browser globals are guarded, so the library no-ops on the server instead of throwing during prerender
- Per-adapter role mapping (realm, resource, or a custom
mapRolesfunction) so the core only ever sees a plainstring[]
Architecture
provideAuth(config, withKeycloak(...))
│ injection tokens
▼
AuthService ──────────── signals: authenticated · claims · roles
│
▼
AuthProvider (port)
├── KeycloakAuthProvider → /keycloak · keycloak-js (optional peer)
├── HttpAuthProvider → /http · your own /login, /refresh, /logout
└── FakeAuthProvider → /testing
authGuard reads accumulated role requirements from route data
authTokenInterceptor reads the URL allowlist, calls getToken() before attachingA provider function, provideAuth, supplies configuration through injection tokens and is composed with an adapter factory such as withKeycloak(). AuthService exposes signals backed by whichever AuthProvider is active. The guard and the interceptor never touch the adapter directly — they go through the service, which is what keeps them provider-agnostic.
The package is built with ng-packagr into separate entry points — the core, /keycloak, /http and /testing — as an Angular Package Format distributable, so an application only bundles the adapter it imports. keycloak-js is an optional peer dependency pulled in only by the Keycloak entry point, and the HTTP adapter is built on Angular's own HttpClient, so the core has zero identity-provider dependencies.
An adapter for your own backend
Not every application has Keycloak. Plenty run their own authentication — a Spring Boot, NestJS, ASP.NET Core or Express backend that checks a password and issues tokens. Version 0.2.0 added withHttpAuth() for exactly that case:
import { withHttpAuth } from '@ismailza/ngx-auth-client/http';
provideAuth(
withHttpAuth({
endpoints: {
signIn: '/api/auth/login',
refresh: '/api/auth/refresh',
logout: '/api/auth/logout',
profile: '/api/auth/me', // optional
},
}),
);The application shows its own login form and calls signIn(); the backend returns a JWT access token and sets a refresh token in an httpOnly cookie. From there, everything else in the library — signals, authGuard, authTokenInterceptor, role checks — works exactly as it does with Keycloak.
// login page
await auth.signIn({ username, password }); // rejects with InvalidCredentialsError on 401
await router.navigateByUrl(sanitizeReturnUrl(route.snapshot.queryParamMap.get('returnUrl')));The contract is small on purpose
| Endpoint | Request | Success |
|---|---|---|
signIn | POST, credentials as JSON | { accessToken, expiresIn? } + sets the refresh cookie |
refresh | POST, refresh cookie only | { accessToken, expiresIn? }, optionally rotating the cookie |
logout | POST, bearer token + cookie | clears the cookie and revokes the refresh token |
profile | GET, bearer token | the user profile |
Backends that answer in another shape don't have to change: mapTokenResponse adapts the response, and common variants like access_token / expires_in are accepted out of the box. The adapter decodes the JWT payload to populate claims() and roles(), but deliberately doesn't verify its signature — the backend verifies the token on every request, and that is the check that protects data.
The session lifecycle is where the work is
Sending a password and storing a token is the easy part. Keeping a session alive correctly is not:
- On page load,
init()callsrefresh. The browser sends the cookie, so an existing session is restored silently; with no cookie, the user is simply signed out. - Before expiry, the token is refreshed in the background, so
claims()stays current even when the app makes no API call. Guard rails — never before half the token's lifetime, never twice within a few seconds — stop a very short-lived token from causing a refresh loop. - Concurrent calls share one refresh. Five requests fired with an expiring token produce one
refreshcall, not five — which matters when the backend rotates refresh tokens and treats reuse as theft. - A flaky network is not a logout. Only a
401(configurable viarejectionStatuses) means "no session". A timeout, a500or a dropped connection is reported as an error and leaves the session alone; a failed background refresh is retried, and the session only ends if the token actually expires first. - The client clock doesn't matter when the backend sends
expiresIn, or when the JWT carries bothiatandexp— the lifetime is measured between two server timestamps.
No open redirect
authGuard sends signed-out users to the login route with the page they wanted in a returnUrl query parameter. Anything in a query string can be crafted by an attacker, so the entry point ships sanitizeReturnUrl(), which only lets in-app paths through. Redirecting after login is the one line every login page writes, and the safe version is the one the documentation shows.
Design decisions
Role mapping belongs to the adapter, not the core
Every provider stores roles somewhere different — Keycloak in realm_access / resource_access, Auth0 in a namespaced custom claim, Cognito in cognito:groups, Entra ID in roles or group GUIDs. The core never sees any of that shape. It only ever sees a flat string[]:
withKeycloak({
url, realm, clientId,
roles: { realm: true, resource: ['my-app'] }, // or: mapRoles: (claims) => string[]
});That single normalization point is what lets authGuard, hasRole(), and every downstream consumer stay provider-agnostic. Teaching the core about one more provider's claim shape would have been the easiest way to quietly make it not provider-agnostic anymore.
Capabilities are feature-detected, never assumed
The core depends on one interface:
export interface AuthProvider {
readonly authenticated: Signal<boolean>;
readonly claims: Signal<Claims | null>;
readonly roles: Signal<readonly string[]>;
init(): Promise<void>;
getToken(): Promise<string>;
login(options?: LoginOptions): Promise<void>;
logout(options?: LogoutOptions): Promise<void>;
}Anything beyond that — registration, account management — lives behind optional capability interfaces (SupportsRegistration, SupportsAccountManagement, …) that the core feature-detects rather than requires. Nothing forces an adapter to pretend it supports functionality it doesn't, and nothing forces every future adapter to stub out methods that provider simply can't do.
The bearer interceptor is an allowlist, and it fails closed
provideAuth(withKeycloak({ ... }), {
bearer: {
urlPattern: /^https:\/\/api\.example\.com(\/.*)?$/i,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
});By default it matches same-origin /api/* requests only. A denylist-shaped interceptor is one misconfiguration away from attaching a user's token to a third-party analytics call. An allowlist that fails closed means the worst outcome of a bad pattern is a missing token — an obvious, loud failure — never a leaked one. It pairs directly with @ismailza/ngx-api-client if you also want a typed HTTP layer underneath.
Tokens live in memory, not Web Storage
Nothing is written to localStorage or sessionStorage, where any XSS payload on the page could read it. Session continuity instead comes from a cookie scripts can't read: Keycloak's own SSO cookie via silent re-authentication, or the httpOnly refresh cookie with the HTTP adapter. PKCE (S256) is on by default and can't be silently disabled by omitting a flag — omission means "use the safe default", not "skip the check".
This is also why client-side role checks are documented explicitly as UX, not authorization: hasRole() decides what a component renders, never what an API accepts.
Guards accumulate role requirements down the route tree
{
path: 'billing',
canActivate: [authGuard],
data: { auth: { allOf: ['admin', 'finance'] } } satisfies AuthRouteData,
loadComponent: () => import('./billing'),
}A requirement set on a parent route applies to every child beneath it, so a section-level canActivate: [authGuard] with a role requirement doesn't need repeating on each leaf route. Unauthenticated users are sent to the provider's login page and returned to the route they originally requested; authenticated users missing the required roles go to a configurable forbidden route instead of a generic redirect.
Testing gets a real adapter, not a mock
import { withFakeAuth } from '@ismailza/ngx-auth-client/testing';
TestBed.configureTestingModule({
providers: [
provideAuth(withFakeAuth({
authenticated: true,
roles: ['admin'],
claims: { sub: 'user-1', name: 'Test User' },
})),
],
});withFakeAuth is a real implementation of the same AuthProvider port, not a stub — guards, interceptors, and components exercise the exact code paths they run in production, without a Keycloak server or a mocked window.location in sight.
SSR is guarded, not assumed away
Browser globals are checked before use throughout the library, so it no-ops on the server rather than throwing during prerender. Auth is one of the more common places SSR breaks in Angular apps that adopt it after the fact — the guard is in the library so it isn't rediscovered per project.
Reading auth state
AuthService is the only thing a component injects:
| Member | Type | Notes |
|---|---|---|
authenticated | Signal<boolean> | |
claims | Signal<Claims | null> | Decoded token payload |
roles | Signal<readonly string[]> | Normalized by the adapter |
profile | Signal<UserProfile | null> | Loaded lazily |
getToken() | Promise<string> | Refreshes if near expiry |
login(opts?) / logout(opts?) | Promise<void> | |
hasRole(r) / hasAnyRole([]) / hasAllRoles([]) | boolean |
getToken() is the one deliberately promise-based member: a token refresh is an operation, not a state. Everything that is state is a signal.
Proven in a full-stack demo
A library's README describes the happy path. To test this one against a real backend — together with its sibling ngx-api-client — I built ngx-libs-demo: an Angular 22 client and an ASP.NET Core 10 API with Identity, JWTs, rotating refresh cookies and PostgreSQL, all behind nginx.
The demo uses withHttpAuth() today and picks its adapter from the server's configuration at startup, so moving to Keycloak is a server setting rather than a client rewrite — the provider-agnostic port doing its job. It exercises session restore, background refresh, guards and roles, remote sign-out, and the open-redirect check. Its library-feedback notes list no issues for ngx-auth-client 0.2.0: the defaults matched an ASP.NET Core backend once the server followed the documented contract.
Compatibility, verified in CI
Verified for Angular 17 through 22. Every release is checked against each supported major in CI — the packed tarball is type-checked with ngc, bundled through a production ng build (which runs the Angular linker over the shipped declarations), and executed in Node, across every entry point.
Testing the tarball rather than the source matters: it is the only way to catch a broken exports map, a missing secondary entry point, or partial declarations the linker can't process — the bugs that only exist in what actually ships.
Results
- Published to npm as
@ismailza/ngx-auth-clientunder MIT, with releases automated by release-please - Two production adapters — Keycloak, and HTTP for home-made backends — behind the same port, with no change to application code between them
- Validated end to end against a real ASP.NET Core backend in ngx-libs-demo
- Every supported Angular major (17–22) verified in CI against the packed npm tarball across every entry point
- Full documentation site at ismailza.github.io/ngx-auth-client, deployed from CI on every push to
main - Zero identity-provider dependencies in the core package —
keycloak-jsis an optional peer - Tokens never touch Web Storage; session continuity comes from an SSO or
httpOnlyrefresh cookie
Project setup
| Area | Tooling |
|---|---|
| Library | Angular, TypeScript, RxJS, Angular Signals |
| Build | ng-packagr → Angular Package Format: core, /keycloak, /http, /testing |
| Tests | Vitest, with a real fake-adapter implementation for consumer testing |
| Quality | ESLint, Prettier, package-contents verification before publish |
| Release | release-please → automated versioning, changelog, and npm publish |
| Docs | Docusaurus, deployed to GitHub Pages from CI |
Roadmap
The Keycloak and HTTP adapters ship today. A generic OIDC adapter (oidc-client-ts), an Auth0 adapter, and route-level scope requirements are next — adapters ship once validated against a real deployment, not before.
The longer story of why I built it is in I Built an Angular Authentication Layer for the Signals Era.
ngx-auth-client
Keycloak authentication for modern Angular — signals, zoneless, standalone.
Documentation · npm · Quick start · API reference
A reactive authentication layer for Angular applications. Exposes auth state as signals, protects routes with a functional guard, and keeps your feature code free of any identity-provider imports.
Ships with a Keycloak adapter and an HTTP adapter for your own backend. Built on a provider-agnostic port, so additional adapters can be added without touching application code.
bootstrapApplication(App, {
providers: [provideAuth(withKeycloak({ url, realm, clientId }))],
});@Component({
template: `@if (auth.authenticated()) {
Hello {{ auth.claims()?.name }}
}`,
})
export class Header {
protected readonly auth = inject(AuthService);
}Why this exists
Angular is signals-first and zoneless by default. Reading authentication state should look like reading any other piece of state in your application.
Today it doesn't. keycloak-angular exposes a signal for events (KEYCLOAK_EVENT_SIGNAL), but authentication state still lives on the Keycloak instance as plain getters — keycloak.authenticated, keycloak.token, keycloak.tokenParsed. Deriving reactive state is left to you: subscribe to the event signal, decide which events matter, and maintain your own signals from them. Every application ends up writing that same layer.
In a zoneless application the getters aren't merely inconvenient, they're invisible:
@if (keycloak.authenticated) { ... } // ✗ never updates — nothing schedules CDZone.js used to paper over this by re-rendering after every async task. Without it, getter-based state simply doesn't participate in change detection.
ngx-auth-client writes that derivation layer once and gives you the state directly:
@if (auth.authenticated()) { ... } // ✓ updates on login, logout, refreshThe second reason is coupling. Auth touches routing, HTTP and templates — the parts of a codebase most expensive to change. This library puts a provider-agnostic port at that boundary, so swapping or upgrading your identity provider stays a one-file change.
When you should use something else
- You need certified, generic OIDC across arbitrary providers →
angular-auth-oidc-client. It is OpenID-certified, mature, and the right call if protocol breadth is your priority. - You're happy deriving state from events yourself →
keycloak-angularis well maintained and closer to the metal. Use it directly if you don't want the abstraction. - You're on Auth0 / Entra ID today → use their first-party SDKs. Adapters here are on the roadmap, but shipped beats planned.
Installation
npm install @ismailza/ngx-auth-client keycloak-jskeycloak-js is an optional peer dependency, required only by the Keycloak adapter. The core package has no identity-provider dependencies.
Quick start
// app.config.ts
import { provideAuth } from '@ismailza/ngx-auth-client';
import { withKeycloak } from '@ismailza/ngx-auth-client/keycloak';
export const appConfig: ApplicationConfig = {
providers: [
provideAuth(
withKeycloak({
url: 'https://auth.example.com',
realm: 'my-realm',
clientId: 'my-app',
}),
),
provideHttpClient(withInterceptors([authTokenInterceptor])),
provideRouter(routes),
],
};That's the complete setup for the common case. Everything else is defaulted: check-sso on load, PKCE S256, automatic token refresh, bearer token attached to same-origin /api/* requests.
→ Quick start · every provideAuth() option
Using your own backend
No Keycloak? withHttpAuth() adapts a backend you run yourself. Your app shows its own login form, and the backend issues a JWT access token plus a refresh token in an httpOnly cookie:
import { withHttpAuth } from '@ismailza/ngx-auth-client/http';
provideAuth(
withHttpAuth({
endpoints: {
signIn: '/api/auth/login',
refresh: '/api/auth/refresh',
logout: '/api/auth/logout',
},
}),
);// login page
await auth.signIn({ username, password }); // rejects with InvalidCredentialsError on 401
await router.navigateByUrl(sanitizeReturnUrl(route.snapshot.queryParamMap.get('returnUrl')));The access token stays in memory, the session survives reloads through the cookie, and concurrent requests share a single refresh. No extra dependency.
Reading auth state
AuthService is the only thing your components inject.
export class ProfileMenu {
private readonly auth = inject(AuthService);
readonly authenticated = this.auth.authenticated; // Signal<boolean>
readonly claims = this.auth.claims; // Signal<Claims | null>
readonly roles = this.auth.roles; // Signal<string[]>
readonly isAdmin = computed(() => this.auth.roles().includes('admin'));
logout() {
this.auth.logout();
}
}| Member | Type | Notes |
|---|---|---|
authenticated |
Signal<boolean> |
|
claims |
Signal<Claims | null> |
Decoded token payload |
roles |
Signal<readonly string[]> |
Normalized by the adapter |
profile |
Signal<UserProfile | null> |
Loaded lazily |
getToken() |
Promise<string> |
Refreshes if near expiry |
login(opts?) / logout(opts?) |
Promise<void> |
|
hasRole(r) / hasAnyRole([]) / hasAllRoles([]) |
boolean |
getToken() is the one deliberately promise-based member: a token refresh is an
operation, not a state. Everything that is state is a signal.
→ Reading auth state · AuthService reference
Protecting routes
export const routes: Routes = [
{
path: 'dashboard',
canActivate: [authGuard],
loadComponent: () => import('./dashboard'),
},
{
path: 'admin',
canActivate: [authGuard],
data: { auth: { anyOf: ['admin', 'owner'] } } satisfies AuthRouteData,
loadComponent: () => import('./admin'),
},
{
path: 'billing',
canActivate: [authGuard],
data: { auth: { allOf: ['admin', 'finance'] } } satisfies AuthRouteData,
loadComponent: () => import('./billing'),
},
];- Unauthenticated users are sent to the provider's login page and returned to the route they requested.
- Authenticated users missing the required roles are redirected to the configured
forbidden route (
/forbiddenby default, set viaprovideAuth({ forbiddenRoute })). - Role requirements accumulate down the route tree, so a requirement on a parent route applies to its children.
→ Protecting routes · authGuard reference
Attaching the token to requests
provideHttpClient(withInterceptors([authTokenInterceptor]));By default the interceptor matches same-origin /api/* requests only. It is an
allowlist — public endpoints, static assets and third-party calls never receive
your token unless you say so:
provideAuth(withKeycloak({ ... }), {
bearer: {
urlPattern: /^https:\/\/api\.example\.com(\/.*)?$/i,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
});Pairs directly with @ismailza/ngx-api-client
if you want a typed HTTP layer on top.
→ Attaching tokens · interceptor reference
Testing
Testing authenticated components shouldn't require a Keycloak server or a mocked
window.location. The package ships a fake adapter:
import { provideAuth } from '@ismailza/ngx-auth-client';
import { withFakeAuth } from '@ismailza/ngx-auth-client/testing';
TestBed.configureTestingModule({
providers: [
provideAuth(
withFakeAuth({
authenticated: true,
roles: ['admin'],
claims: { sub: 'user-1', name: 'Test User' },
}),
),
],
});The fake is a real implementation of the same port, not a stub — guards, interceptors and components exercise the same code paths they do in production.
→ Testing guide · testing entry point
Adding a provider
The core depends on a single interface:
export interface AuthProvider {
readonly authenticated: Signal<boolean>;
readonly claims: Signal<Claims | null>;
readonly roles: Signal<readonly string[]>;
init(): Promise<void>;
getToken(): Promise<string>;
login(options?: LoginOptions): Promise<void>;
logout(options?: LogoutOptions): Promise<void>;
}Provider-specific behaviour lives behind optional capability interfaces
(SupportsRegistration, SupportsAccountManagement, …), which the core feature-detects
rather than requiring. Nothing forces an adapter to pretend it has functionality it
doesn't.
Role mapping belongs to the adapter. Every provider stores roles somewhere different
— Keycloak in realm_access/resource_access, Auth0 in a namespaced custom claim,
Cognito in cognito:groups, Entra ID in roles or group GUIDs. The adapter normalizes;
the core only ever sees string[]:
withKeycloak({
url,
realm,
clientId,
roles: { realm: true, resource: ['my-app'] }, // or: mapRoles: (claims) => string[]
});→ Writing a custom provider · ports and capabilities
Security notes
- Tokens are held in memory only. Nothing is written to
localStorageorsessionStorage, where any XSS payload on the page could read them. Session continuity comes from the provider's own SSO cookie via silent re-authentication. - PKCE (
S256) is on by default and cannot be silently disabled by omission. - The bearer interceptor is an allowlist. A misconfigured pattern fails closed (no token attached) rather than leaking a token to a third-party host.
- SSR-safe. Browser globals are guarded; the library no-ops on the server rather than throwing during prerender.
Client-side role checks are UX, not authorisation — enforce every rule that matters in your API.
Found a security issue? See SECURITY.md — please report privately rather than opening a public issue.
Angular support
| Angular | Supported |
|---|---|
| 17 – 22 | ✅ |
Signals-based state requires Angular 17+. Zoneless is supported but not required.
Every release is verified against each supported major in CI — the packed tarball is
type-checked with ngc, bundled through a production ng build (which runs the Angular
linker over the shipped declarations) and executed in Node, for all three entry points.
Documentation
Full documentation is at ismailza.github.io/ngx-auth-client.
| Section | Pages |
|---|---|
| Getting started | Installation · Quick start |
| Guides | Configuration · Reading auth state · Protecting routes · Attaching tokens · Keycloak adapter · Your own backend · Capabilities · Server-side rendering · Testing · Custom provider · Security |
| API reference | provideAuth() · AuthService · authGuard · authTokenInterceptor · Keycloak entry point · Testing entry point · Ports · Models · Injection tokens |
The site is built from website/ and deploys on every push to main.
Roadmap
- Keycloak adapter
- HTTP adapter for your own backend
- Generic OIDC adapter (
oidc-client-ts) - Auth0 adapter
- Route-level scope requirements
Adapters ship when they're validated against a real deployment, not before.
Support
Maintained on a best-effort basis alongside my other work. Issues are read and answered; feature requests outside the scope above are likely to be declined with a reason rather than left open.
License
Support the Project
If you find this library useful, consider giving it a ⭐ on GitHub. It helps others discover the project and motivates future development.
MIT License Copyright (c) 2026 Ismail ZAHIR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.