|
| 1 | +# Auth0-Angular v2 Migration Guide |
| 2 | +With the v2 release of Auth0-Angular, we have improved both the performance and developer experience by incorporating Auth0-SPA-JS v2 while trying to limit the amount of breaking changes. However, as with any major version bump, v2 of Auth0-Angular contains a set of breaking changes. |
| 3 | + |
| 4 | +Please review this guide thoroughly to understand the changes required to migrate your application to v2. |
| 5 | + |
| 6 | +- [Polyfills and supported browsers](#polyfills-and-supported-browsers) |
| 7 | +- [Public API changes](#public-api-changes) |
| 8 | + - [Introduction of authorizationParams](#introduction-of-authorizationparams) |
| 9 | + - [Introduction of logoutParams](#introduction-of-logoutparameters) |
| 10 | + - [buildAuthorizeUrl has been removed](#buildauthorizeurl-has-been-removed) |
| 11 | + - [buildLogoutUrl has been removed](#buildlogouturl-has-been-removed) |
| 12 | + - [redirectMethod has been removed from loginWithRedirect](#redirectmethod-has-been-removed-from-loginwithredirect) |
| 13 | + - [localOnly has been removed from logout](#localonly-has-been-removed-from-logout) |
| 14 | + - [logout no longer returns a promise](#logout-no-longer-returns-a-promise) |
| 15 | + - [ignoreCache on getTokenSilentlyhas been replaced by cacheMode](#ignorecache-on-gettokensilentlyhas-been-replaced-by-cachemode) |
| 16 | + - [application/x-www-form-urlencoded is used by default instead of application/json](#applicationx-www-form-urlencoded-is-used-by-default-instead-of-applicationjson) |
| 17 | + - [No more iframe fallback by default when using refresh tokens](#no-more-iframe-fallback-by-default-when-using-refresh-tokens) |
| 18 | + - [getUser and getIdTokenClaims have been removed](#getuser-and-getidtokenclaims-have-been-removed) |
| 19 | + - [Changes to default scopes (profile and email)](#changes-to-default-scopes-profile-and-email) |
| 20 | + - [advancedOptions and defaultScope are removed](#advancedoptions-and-defaultscope-are-removed) |
| 21 | + |
| 22 | +## Polyfills and supported browsers |
| 23 | + |
| 24 | +As [Microsoft has dropped support for IE11](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know), Auth0-SPA-JS v2 no longer includes any polyfills in its bundle, as all of these polyfills were for IE11. Therefore <u>the Auth0-Angular SDK, which uses Auth0-SPA-JS internally, no longer support IE11 as of v2</u>. |
| 25 | + |
| 26 | +> :information_source: With Angular having dropped support for IE itself in Angular 13, and Angular 13 currently being the lowest supported version of Angular, this shouldn't impact any application that's using a version supported by the Angular team. |
| 27 | +
|
| 28 | +The following is the list of polyfills that got removed. If you would need any of these, you will need to include these in your application. |
| 29 | + |
| 30 | +- [AbortController](https://www.npmjs.com/package/abortcontroller-polyfill): Used to polyfill [AbortController on IE11, Opera Mini, and some mobile-specific browsers](https://caniuse.com/?search=abortcontroller). |
| 31 | +- [Promise](https://www.npmjs.com/package/promise-polyfill): Used to polyfill [Promise on IE11 and Opera Mini](https://caniuse.com/promises) |
| 32 | +- [Core-js](https://www.npmjs.com/package/core-js): Used to polyfill a couple of things, also mostly on IE11, Opera Mini, and some mobile-specific browsers: |
| 33 | + - [string/startsWith](https://caniuse.com/?search=startsWith) |
| 34 | + - [string/includes](https://caniuse.com/es6-string-includes) |
| 35 | + - [set](https://caniuse.com/mdn-javascript_builtins_set) |
| 36 | + - [symbol](https://caniuse.com/mdn-javascript_builtins_symbol) |
| 37 | + - [array/from](https://caniuse.com/mdn-javascript_builtins_array_from) |
| 38 | + - [array/includes](https://caniuse.com/array-includes) |
| 39 | +- [fast-text-encoding](https://www.npmjs.com/package/fast-text-encoding): Used to polyfill TextEncoder and TextDecoder on IE11 and Opera Mini. |
| 40 | +- [unfetch](https://www.npmjs.com/package/unfetch): Used to [ponyfill fetch on IE11](https://caniuse.com/?search=fetch). |
| 41 | + |
| 42 | +Because of this, we have <u>dropped 60% in bundle size</u> for Auth0-SPA-JS, which is a core dependency of the Auth0-Angular SDK. Ensuring your users have a better experience when integrating Auth0 using the Auth0-Angular SDK. |
| 43 | + |
| 44 | +## Public API Changes |
| 45 | + |
| 46 | +With the release of this new major version, a couple of changes were made that affect the public API of the Auth0-Angular SDK. Most of these should be noticed by TypeScript. However, it’s advised to take the time to go through this list thoroughly. |
| 47 | + |
| 48 | +### Introduction of `authorizationParams` |
| 49 | + |
| 50 | +A breaking change that will affect pretty much everyone is the introduction of `authorizationParams`, a more structured approach to providing parameters - including custom parameters - to Auth0. |
| 51 | + |
| 52 | +In v1, objects passed to our methods are always a mix of properties used for configuring the SDK and properties with the sole purpose to pass through to Auth0. |
| 53 | + |
| 54 | +```ts |
| 55 | +@NgModule({ |
| 56 | + // ... |
| 57 | + imports: [ |
| 58 | + AuthModule.forRoot({ |
| 59 | + domain: '', |
| 60 | + clientId: '', |
| 61 | + audience: '', |
| 62 | + redirectUri: '' |
| 63 | + }), |
| 64 | + ], |
| 65 | + // ... |
| 66 | +}) |
| 67 | +export class AppModule {} |
| 68 | +``` |
| 69 | +```ts |
| 70 | +@Component({ /* ... */ }) |
| 71 | +export class AppComponent { |
| 72 | + constructor(public auth: AuthService) {} |
| 73 | + |
| 74 | + loginWithRedirect() { |
| 75 | + this.auth.loginWithRedirect({ |
| 76 | + appState: { |
| 77 | + key: value // state to restore when getting redirected back |
| 78 | + } |
| 79 | + screen_hint: 'signup', // 1st-class property to send to Auth0 |
| 80 | + any_custom_property: 'value' // Any additional custom property to send to Auth0 |
| 81 | + }); |
| 82 | + } |
| 83 | +``` |
| 84 | +
|
| 85 | +With v2 of our SDK, we have improved the API by separating those properties used to configure the SDK, from properties that are sent to Auth0. The SDK configuration properties will stay on the root, while any property that should be sent to Auth0 should be set on `authorizationParams`. |
| 86 | +
|
| 87 | +```ts |
| 88 | +@NgModule({ |
| 89 | + // ... |
| 90 | + imports: [ |
| 91 | + AuthModule.forRoot({ |
| 92 | + domain: '', |
| 93 | + clientId: '', |
| 94 | + authorizationParams: { |
| 95 | + audience: '', |
| 96 | + redirect_uri: '' |
| 97 | + } |
| 98 | + }), |
| 99 | + ], |
| 100 | + // ... |
| 101 | +}) |
| 102 | +export class AppModule {} |
| 103 | +``` |
| 104 | +```ts |
| 105 | +@Component({ /* ... */ }) |
| 106 | +export class AppComponent { |
| 107 | + constructor(public auth: AuthService) {} |
| 108 | + |
| 109 | + loginWithRedirect() { |
| 110 | + this.auth.loginWithRedirect({ |
| 111 | + appState: { |
| 112 | + key: value // state to restore when getting redirected back |
| 113 | + }, |
| 114 | + authorizationParams: { |
| 115 | + screen_hint: 'signup', |
| 116 | + any_custom_property: 'value' |
| 117 | + } |
| 118 | + }); |
| 119 | + } |
| 120 | +``` |
| 121 | +
|
| 122 | +The above changes affect the following methods: |
| 123 | +
|
| 124 | +- loginWithRedirect |
| 125 | +- loginWithPopup |
| 126 | +- getAccessTokenWithPopup |
| 127 | +- getAccessTokenSilently |
| 128 | +
|
| 129 | +If you are using any of the above methods in your application(s), ensure to update all of these as mentioned above. |
| 130 | +
|
| 131 | +### Introduction of `logoutParams` |
| 132 | +
|
| 133 | +In v1 of the SDK, `logout` can be called with an object containing a number of properties, both a mix between properties used to configure the SDK as well as those used to pass through to Auth0. |
| 134 | +
|
| 135 | +With v2, logout now takes an object that can only contain three properties, `clientId`, `openUrl` and `logoutParams`. |
| 136 | +
|
| 137 | +Any property, apart from clientId, that you used to set on the root of the object passed to `logout` should now be set on `logoutParams` instead. |
| 138 | +
|
| 139 | +```ts |
| 140 | +@Component({ /* ... */ }) |
| 141 | +export class AppComponent { |
| 142 | + constructor(public auth: AuthService) {} |
| 143 | + |
| 144 | + logout() { |
| 145 | + this.auth.logout({ |
| 146 | + clientId: '', |
| 147 | + logoutParams: { |
| 148 | + federated: true / false, |
| 149 | + returnTo: '', |
| 150 | + any_custom_property: 'value' |
| 151 | + } |
| 152 | + }); |
| 153 | + } |
| 154 | +``` |
| 155 | +
|
| 156 | +### `buildAuthorizeUrl` has been removed |
| 157 | +
|
| 158 | +In v1, we introduced `buildAuthorizeUrl` for applications that couldn’t rely on `window.location.assign` to redirect to Auth0 when calling `loginWithRedirect`, a typical example is for people using v1 of our SDK with Ionic: |
| 159 | +
|
| 160 | +```ts |
| 161 | +@Component({ /* ... */ }) |
| 162 | +export class LoginComponent { |
| 163 | + constructor(public auth: AuthService) {} |
| 164 | + |
| 165 | + login() { |
| 166 | + this.auth |
| 167 | + .buildAuthorizeUrl() |
| 168 | + .pipe(mergeMap((url) => Browser.open({ url, windowName: '_self' }))) |
| 169 | + .subscribe(); |
| 170 | + } |
| 171 | +} |
| 172 | +``` |
| 173 | +
|
| 174 | +With v2, we have removed `buildAuthorizeUrl`. This means that the snippet above will no longer work, and you should update your code by using `openUrl` instead. |
| 175 | +
|
| 176 | +```ts |
| 177 | +@Component({ /* ... */ }) |
| 178 | +export class LoginComponent { |
| 179 | + constructor(public auth: AuthService) {} |
| 180 | + |
| 181 | + login() { |
| 182 | + this.auth |
| 183 | + .loginWithRedirect({ |
| 184 | + openUrl: (url) => Browser.open({ url, windowName: '_self' }) |
| 185 | + }) |
| 186 | + .subscribe(); |
| 187 | + } |
| 188 | +} |
| 189 | +``` |
| 190 | +
|
| 191 | +The above snippet aligns more with the intent, using our SDK to login but relying on Capacitor (or any other external browser) to do the actual redirect. |
| 192 | +
|
| 193 | +### `buildLogoutUrl` has been removed |
| 194 | +
|
| 195 | +In v1, we introduced `buildLogoutUrl` for applications that are unable to use `window.location.assign` when logging out from Auth0, a typical example is for people using v1 of our SDK with Ionic: |
| 196 | +
|
| 197 | +```ts |
| 198 | +@Component({ /* ... */ }) |
| 199 | +export class LogoutComponent { |
| 200 | + constructor(public auth: AuthService) {} |
| 201 | + |
| 202 | + logout() { |
| 203 | + this.auth |
| 204 | + .buildLogoutUrl({ returnTo: '...' }) |
| 205 | + .pipe( |
| 206 | + tap((url) => { |
| 207 | + this.auth.logout({ localOnly: true }); |
| 208 | + Browser.open({ url }); |
| 209 | + }) |
| 210 | + ) |
| 211 | + .subscribe(); |
| 212 | + } |
| 213 | +} |
| 214 | +``` |
| 215 | +
|
| 216 | +With v2, `buildLogoutUrl` has been removed and you should update any code that is not able to rely on `window.location.assign` to use `openUrl` when calling `logout`: |
| 217 | +
|
| 218 | +```ts |
| 219 | +@Component({ /* ... */ }) |
| 220 | +export class LogoutComponent { |
| 221 | + constructor(public auth: AuthService) {} |
| 222 | + |
| 223 | + logout() { |
| 224 | + this.auth |
| 225 | + .logout({ |
| 226 | + openUrl: (url)=> Browser.open({ url }) |
| 227 | + }) |
| 228 | + .subscribe(); |
| 229 | + } |
| 230 | +} |
| 231 | +``` |
| 232 | +
|
| 233 | +This method was removed because, when using our SDK, the logout method is expected to be called regardless of the browser used. Instead of calling both `logout` and `buildLogoutUrl`, you can now change the redirect behaviour when calling `logout`. |
| 234 | +
|
| 235 | +### `redirectMethod` has been removed from `loginWithRedirect` |
| 236 | +
|
| 237 | +In v1, `loginWithRedirect` takes a `redirectMethod` that can be set to any of `assign` and `replace`, allowing the users to control whether the SDK should redirect using `window.location.assign` or `window.location.replace`. |
| 238 | +
|
| 239 | +```ts |
| 240 | +this.auth.loginWithRedirect({ |
| 241 | + redirectMethod: 'replace' |
| 242 | +}); |
| 243 | +``` |
| 244 | +
|
| 245 | +With the release of v2, we have removed `redirectMethod`. If you want to use anything but `window.location.assign` to handle the redirect to Auth0, you should implement `openUrl`: |
| 246 | +
|
| 247 | +```ts |
| 248 | +this.auth.loginWithRedirect({ |
| 249 | + openUrl: (url) => { |
| 250 | + // Open url in the browser |
| 251 | + } |
| 252 | +}); |
| 253 | +``` |
| 254 | +
|
| 255 | +### `localOnly` has been removed from `logout` |
| 256 | +
|
| 257 | +In v1, `logout` took a `localOnly` option to prevent logging the user out of Auth0 when logging out from your application. |
| 258 | +
|
| 259 | +```ts |
| 260 | +this.auth.logout({ |
| 261 | + localOnly: true |
| 262 | +}); |
| 263 | +``` |
| 264 | +
|
| 265 | +With v2, we have removed the `localOnly` options, instead you should set `openUrl` to `false`: |
| 266 | +
|
| 267 | +```ts |
| 268 | +this.auth.logout({ |
| 269 | + openUrl: false |
| 270 | +}); |
| 271 | +``` |
| 272 | +
|
| 273 | +### `logout` no longer returns a Promise |
| 274 | +All methods in the SDK return an Observable, while in v1 `logout` returned a `Promise`. With v2, we have reworked this so that the `logout` method aligns with the other methods in the SDK. |
| 275 | +
|
| 276 | +### `ignoreCache` on `getAccessTokenSilently` has been replaced by `cacheMode` |
| 277 | +
|
| 278 | +In v1, users can bypass the cache when calling `getAccessTokenSilently` by passing `ignoreCache: true`. |
| 279 | +
|
| 280 | +```ts |
| 281 | +this.auth.getAccessTokenSilently({ ignoreCache: true }).subscribe(token => { /* ... */ }); |
| 282 | +``` |
| 283 | +
|
| 284 | +With v2, we wanted to add the ability to only retrieve a token from the cache, without contacting Auth0 if no token was found. To do so, we have removed the `ignoreCache` property and replaced it with `cacheMode` that can take any of the following three values: |
| 285 | +
|
| 286 | +- **on** (default): read from the cache caching, but fall back to Auth0 as needed |
| 287 | +- **off**: ignore the cache, instead always call Auth0 |
| 288 | +- **cache-only**: read from the cache, don’t fall back to Auth0 |
| 289 | +
|
| 290 | +Any code that was previously using `ignoreCache: true` should be changed to use `cacheMode: 'off'`: |
| 291 | +
|
| 292 | +```ts |
| 293 | +this.auth.getAccessTokenSilently({ cacheMode: 'off' }).subscribe(token => { /* ... */ }); |
| 294 | +``` |
| 295 | +
|
| 296 | +### `application/x-www-form-urlencoded` is used by default instead of `application/json` |
| 297 | +
|
| 298 | +Auth0’s token endpoint supports both `application/x-www-form-urlencoded` and `application/json` content types. However, using `application/x-www-form-urlencoded` provides a small performance benefit. |
| 299 | +
|
| 300 | +In v1 of the SDK, the default was to send request to /oauth/token using json, allowing to opt-in to use x-www-form-urlencoded by setting the `useFormData` flag to _true_. |
| 301 | +
|
| 302 | +With v2, we have flipped the default value for `useFormData` to **true**, meaning we will be sending requests to Auth0’s token endpoint using `application/x-www-form-urlencoded` as the content type by default. |
| 303 | +
|
| 304 | +> :warning: This can affect existing rules and actions, and it’s important to ensure all your actions still work as expected after upgrading to v2. |
| 305 | +> To restore the original behaviour, you can set `useFormData` to **false**, and your rules and actions should continue to work as before. |
| 306 | +
|
| 307 | +### No more iframe fallback by default when using refresh tokens |
| 308 | +
|
| 309 | +When using refresh tokens in v1, we fall back to using iframes whenever a refresh token exchange would fail. This has caused problems before in environments that do not support iframes, and we have specifically introduced `useRefreshTokensFallback` to be able to opt-out of falling back to iframes in the case a refresh_grant fails. |
| 310 | +
|
| 311 | +With v2, we have flipped the default value for `useRefreshTokensFallback` to false so we do not fall back to using iframes by default when `useRefreshTokens` is `true`, and the refresh token exchange fails. |
| 312 | +
|
| 313 | +If you want to restore the original behaviour, and still fall back to iframes when the refresh token exchange fails, you can set `useRefreshTokensFallback` to true. |
| 314 | +
|
| 315 | +### `getUser` and `getIdTokenClaims` have been removed |
| 316 | +
|
| 317 | +With v1 of our SDK, both `getUser` and `getIdTokenClaims` supported optional audience and scope parameters when retrieving the user profile. |
| 318 | +
|
| 319 | +```ts |
| 320 | +this.auth.getUser().subscribe(user => { /* ... */ }); |
| 321 | +this.auth.getUser({ audience, scope }).subscribe(user => { /* ... */ }); |
| 322 | + |
| 323 | +this.auth.getIdTokenClaims().subscribe(claims => { /* ... */ }); |
| 324 | +this.auth.getIdTokenClaims({ audience, scope }).subscribe(claims => { /* ... */ }); |
| 325 | +``` |
| 326 | +
|
| 327 | +As an application should only have one user, it makes little sense to be passing these parameters when trying to retrieve the current user. |
| 328 | +
|
| 329 | +With v2, both `getUser` and `getIdTokenClaims` have been removed, and should be replaced by the corresponding Observables. |
| 330 | +
|
| 331 | +```ts |
| 332 | +this.auth.user$.subscribe(user => { /* ... */ }); |
| 333 | + |
| 334 | +this.auth.idTokenClaims$.subscribe(claims => { /* ... */ }); |
| 335 | +``` |
| 336 | +
|
| 337 | +### Changes to default scopes (profile and email) |
| 338 | +
|
| 339 | +Our SDK defaults to requesting `openid profile email` as the scopes. However, when the user explicitly sets the `scope` when configuring the SDK by importing the `AuthModule`, v1 would still include `openid profile email` as well. |
| 340 | +
|
| 341 | +With v2, we have reworked this to still default to `openid profile email` when the scope property has been omitted, but only include `openid` when the user sets a scope explicitly. |
| 342 | +
|
| 343 | +This means that the following code in v1: |
| 344 | +
|
| 345 | +```ts |
| 346 | +AuthModule.forRoot({ |
| 347 | + scope: 'scope1' |
| 348 | +}) |
| 349 | +``` |
| 350 | +
|
| 351 | +Needs to be updated to explicitly include the `profile email` scopes to achieve the same in v2: |
| 352 | +
|
| 353 | +```ts |
| 354 | +AuthModule.forRoot({ |
| 355 | + scope: 'profile email scope1' |
| 356 | +}) |
| 357 | +``` |
| 358 | +
|
| 359 | +#### advancedOptions and defaultScope are removed |
| 360 | +
|
| 361 | +With v1 of our SDK, users can set both `scope: '...'` and `advancedOptions: { defaultScope: '...' }` when configuring the SDK by importing the ``AuthModule`. As this has proven to be confusing, with v2 we have decided to drop `defaultScope` altogether. As this was its own property, we have also removed `advancedOptions`. Any code that used to rely on `defaultScope` will need to move those scopes into `scope` instead: |
| 362 | + |
| 363 | +```ts |
| 364 | +AuthModule.forRoot({ |
| 365 | + advancedOptions: { defaultScope: 'email' } |
| 366 | + scope: 'scope1' |
| 367 | +}) |
| 368 | +``` |
| 369 | + |
| 370 | +Will need to move those scopes into `scope` instead: |
| 371 | + |
| 372 | +```ts |
| 373 | +AuthModule.forRoot({ |
| 374 | + scope: 'email scope1' |
| 375 | +}), |
| 376 | +``` |
| 377 | + |
| 378 | +As you can see, `scope` becomes a merged value of the previous `defaultScope` and `scope`. |
0 commit comments