Closed magwas closed 1 year ago
Thanks for reaching out. Can you help make me understand what makes you believe this SDK supports cordova?
Having said that, happy to try and understand what's stopping you from using it and accepting any PR that makes it work without breaking other users.
Well, probably all the bad PR which says that with cordova you can run any angular app :)
In the meantime I did realize that I need something to catch the callback url. Found this tutorial: https://auth0.com/docs/quickstart/native/ionic-angular/01-login
As I understand the main point is to have an in-app browser where we can 'catch' url opens, and do our thing (giving the info to auth0) instead of following the actual url.
According to your documentation, the browser of capacitor does exactly this.
And the opening of the app is indeed done within an in-app browser on android.
But it seems that the appUrlOpen listener does not kick in. Neither in the browser nor in android. The android app was compiled in the platforms/android dir created by capacitor using gradlew build.
This is my AppAuthButton.ts:
import { Component, Inject, NgZone, OnInit } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { DOCUMENT } from '@angular/common';
import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';
import { REDIRECT_URI } from '../module';
@Component({
selector: 'app-auth-button',
template: `
<ng-container *ngIf="auth.isAuthenticated$ | async; else loggedOut">
<button id="logout_button" (click)="auth.logout({ logoutParams: { returnTo: document.location.href } })">
Log out
</button>
</ng-container>
<ng-template #loggedOut>
<button id="login_button" (click)="login()">Log in</button>
</ng-template>
`,
styles: [],
})
export class AuthButtonComponent implements OnInit {
constructor
(@Inject(DOCUMENT) public document: Document,
readonly ngZone: NgZone,
readonly auth: AuthService
) { }
ngOnInit(): void {
console.log("ngOnInit")
const promise = App.addListener('appUrlOpen', ({ url }) => {
console.log("appUrlOpen", url)
this.ngZone.run(() => {
if (url.startsWith(REDIRECT_URI)) {
// If the URL is an authentication callback URL..
if (
(url.includes('state=') &&
(url.includes('error=') || url.includes('code=')))
) {
this.auth
.handleRedirectCallback(url)
.subscribe((): void => { void Browser.close() });
} else {
void Browser.close();
}
}
});
});
console.log("ngOnInit", promise)
}
login(): void {
this.auth
.loginWithRedirect({
async openUrl(url: string) {
await Browser.open({ url, windowName: '_self' });
}
})
.subscribe();
}
}
My module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './routing';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { HeroesComponent } from './UI/heroes/heroes.component';
import { HeroeditorComponent } from './UI/heroeditor/heroeditor.component';
import { HeroitemComponent } from './UI/heroitem/heroitem.component';
import { HeroListComponent } from './UI/herolist/herolist.component';
import { HeroFilterComponent } from './UI/herofilter/herofilter.component';
import { StoreModule } from '@ngrx/store';
import { ObtainHeroesService } from './services/ObtainHeroesService';
import { Synchronizer } from '../com.kodekonveyor.common/Synchronizer';
import { AuthHttpInterceptor, AuthModule } from '@auth0/auth0-angular';
import { AuthButtonComponent } from './UI/AppAuthButton';
import { UserProfileComponent } from './UI/UserProfileComponent';
import { GenericErrorHandler } from 'src/com.kodekonveyor.common/GenericErrorHandler';
import { repository } from './repositories/Repository';
import { EffectsModule } from '@ngrx/effects';
import { ChangeUserEffect } from './effects/ChangeUserEffect';
import { SaveHeroService } from './services/SaveHeroService';
import { CreateHeroEffect } from './effects/CreateHeroEfffect';
import { StoreHeroesEffect } from './effects/StoreHeroesEffect';
import { StoreHeroesService } from './services/StoreHeroesService';
import { ObtainConfigEffect } from './effects/ObtainConfigEffect';
import { FollowAuthenticatedStateEffect } from './effects/FollowAuthenticatedStateEffect';
export const REDIRECT_URI = 'com.kodekonveyor.angulartest://kode-konveyor.eu.auth0.com/capacitor/com.kodekonveyor.angulartest/callback';
@NgModule({
declarations: [
HeroesComponent,
HeroeditorComponent,
HeroitemComponent,
HeroListComponent,
HeroFilterComponent,
AuthButtonComponent,
UserProfileComponent
],
imports: [
EffectsModule.forRoot([ChangeUserEffect, CreateHeroEffect, FollowAuthenticatedStateEffect, ObtainConfigEffect, StoreHeroesEffect]),
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
StoreModule.forRoot({ r: repository }),
AuthModule.forRoot({
domain: 'kode-konveyor.eu.auth0.com',
clientId: 'OqUGGMvs9Ch8yitD3sf2lm6mN61MZqPw',
authorizationParams: {
redirect_uri: REDIRECT_URI,
audience: 'https://test.kodekonveyor.com/angulartest',
scope: 'read:current_user'
},
httpInterceptor: {
allowedList: [
{
uri: 'https://test.kodekonveyor.com/angulartest/api/v1/hero',
tokenOptions: {
authorizationParams: {
audience: 'https://test.kodekonveyor.com/angulartest',
scope: 'read:current_user'
}
}
},
{
uri: 'http://localhost:9090/angulartest/api/v1/hero',
tokenOptions: {
authorizationParams: {
audience: 'https://test.kodekonveyor.com/angulartest',
scope: 'read:current_user'
}
}
},
{
uri: '/angulartest/api/v1/hero',
tokenOptions: {
authorizationParams: {
audience: 'https://test.kodekonveyor.com/angulartest',
scope: 'read:current_user'
}
}
}
]
}
})
],
providers: [
GenericErrorHandler,
ObtainHeroesService,
SaveHeroService,
Synchronizer,
StoreHeroesService,
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
],
bootstrap: [
AuthButtonComponent,
UserProfileComponent,
HeroesComponent
]
})
export class Angulartest { }
Thanks for the update. Capacitor is something we actively support and test, we even have our own sample for using this sdk with capacitor: https://github.com/auth0-samples/auth0-ionic-samples/tree/main/angular
That sample app should work, can you verify if you can run that and login succesfully?
Also be sure to register your custom scheme with the android app.
I had to fundamentally change the routing scheme and prepare my original base module for lazy loading to get my app work with Ionic/Capacitor.
I guess you could help your customers by adding a couple of sentences to the end of the angular howto. About going multiplatform, referring to the capacitor example and placing an appropriate warning about the need to do the above steps.
We are always open to improve any documentation, but can you elaborate on what exact steps you think would be required that are not part of the linked sample application, which does work for me without any modifications.
Did you try the linked sample application yourself? If so, did you experience any issues?
The sample application did work, though jslint gives quite a lot warnings for it when configured to be strict.
I already had an application. With a simple routing, even simpler than what the sample in auth0-angular-samples has. Auth0-ionic-samples/angular have an ionic routing module, its main app html have a single ion-app tag with ion-router-outlet tag. It lazy-loads the part which is the actual application. I am not sure whether it is essential for being able to catch the callback with capacitor, but I have followed that routing strategy in my app. Because the lazy loading and the ionic specific tags I had to change some things in my module.ts:
imports: [
- EffectsModule.forRoot([ChangeUserEffect, CreateHeroEffect, FollowAuthenticatedStateEffect, ObtainConfigEffect, StoreHeroesEffect]),
- BrowserModule,
+ EffectsModule.forFeature([ChangeUserEffect, CreateHeroEffect, FollowAuthenticatedStateEffect, ObtainConfigEffect, StoreHeroesEffect]),
+ CommonModule,
IonicModule.forRoot(),
- StoreModule.forRoot({ r: repository }),
+ StoreModule.forFeature("r", repository),
[...]
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
I changed the routing module for that module as well, to show my app on '' (which is called up in the base module as '/home'.
And add the appropriate forRoot calls in the new app module.
And ofc I had to tweak the callback, etc. parameters in my config at auth0, my auth0 module config in the app. I have also changed the application in the auth0 page to native from single page app.
update: while it works on the web now, and starts up in android, the callback still does not kick in in my app. I hope it is just some typos in some config.
I think the issues regarding forRoot and forFeature as not related to our SDK or Ionic. When lazy loading, you need to ensure u correctly use forRoot and forFeature, this is related to how Angular handles Dependency Injection and InjectionContexts when using Lazy Loading. I do understand it's complicated, but I do not see how it relates to our SDK.
If you believe our docs can be improved, we are always happy to review a PR.
update: while it works on the web now, and starts up in android, the callback still does not kick in in my app. I hope it is just some typos in some config.
Did u correctly register the custom scheme in android?
I have also changed the application in the auth0 page to native from single page app.
That should not be needed, any idea why you had to make that change?
My point is that probably it is a common route to make auth0 work with a web-based angular app, then make it multiplatform. When doing so, one faces a change which is probably triggered by the different ways one should use your lib in different scenarios. While those changes are indeed related to the structure change of the app, and on face value nothing to do with using your lib, the need of those changes are triggered by using your lib. (I am not entirely sure whether it is just because the developers of the different samples used different routing approaches or because the routing approach in the capacitor sample is indeed needed to be able to catch the callback url. So yes, an experienced capacitor developer probably would have no problem to figure out this move, but the main point is that those who will have problems figuring it out are not experienced in capacitor, as just trying to move their app to a new platform. And yes, the code - the best documentation - is there, but figuring out what is important is sometimes hard. It is your choice how much you document and how much you leave to your customers to figure out from source. I just thought knowing these things in advance would have helped me. I have figured out a bugreport-worth info on why the android platform still does not work for me. I report it separately, as this is a documentation feature request now, and that is not.
I am not entirely sure whether it is just because the developers of the different samples used different routing approaches or because the routing approach in the capacitor sample is indeed needed to be able to catch the callback url.
Ionic applications should use ion-router-outlet
, which is optimized for providing a stacked layout you want with mobile apps, see https://ionicframework.com/docs/api/router-outlet.
That said, I am convinced it also works with the regular angular router outlet (as the ion router outlet is a wrapper around the original), but that might not result in the best user experience on mobile.
I am happy to look into a reproduction to try and help you solve your issue with the regular router outlet, but currently it's not clear what is causing the issues and without a reproduction of the issues you are experiencing it's hard for us to help. Please open another issue with a reproduction and I can look into it.
That said, and as mentioned, I believe our documentation is not the place to explain the use of forRoot
and forFeature
, as that can cause issues with all kind of libraries, and not just ours.
but the main point is that those who will have problems figuring it out are not experienced in capacitor, as just trying to move their app to a new platform.
Our documentation is not the place to teach people capacitor. As much as we try to do our best to improve our documentation, we have to draw a line somewhere and assume people learn the platform on the corresponding channels or websites dedicated for that.
Closing this, as I believe there is nothing for us to action based on the information provided.
Describe the problem
auth0 authentication does not work in the app when compiled with cordova to android. the app does not recognize the callback url https://localhost/index.html as its own
What was the expected behavior?
Work the same as in a browser.
Reproduction
Environment
auth0-angular
used: 2.0.1