Skip to content
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

[DOC] Missing mobile guidelines #30

Open
1 task done
PhilippRoessner opened this issue Feb 18, 2023 · 17 comments
Open
1 task done

[DOC] Missing mobile guidelines #30

PhilippRoessner opened this issue Feb 18, 2023 · 17 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@PhilippRoessner
Copy link

Documentation can be submitted with pull requests

  • I know that I can edit the docs myself but prefer to file this issue instead

Docs URL

https://badisi.github.io/auth-js/site/documentation/getting-started/mobile

Description

First I'd like to thank you for this great library! It is exactly what I was waiting for!

I am trying to migrate from a complicated mix of oidc-client-ts for web (angular) and ionic-appauth for our capacitor app to this solution. So far the Web Part works great as a proof of concept.

When I try to login on the android capacitor app, I get the following warning in the console:

 [@badisi/auth-js] This application is currently using a non recommended browser plugin.

ⓘ Please follow the recommended guide and use `@badisi/capacitor-browsertab` instead.

When I look at the code, it seems like this is not required, is that right?

My Authprovider (Identity Server 4) throws an error when I am using the capacitor android client and I am wondering if this might be connected? I remember from my previous implementations that the url scheme was handled differently on ios and on android. One of them had to use "capacitor://" and the other "localhost://" or something like that.

Currently I set the mobileScheme property on the parameter object for initAuth and the same value for the intent-filter in the androidmanifest.xml. Is that all it takes?

@PhilippRoessner PhilippRoessner added documentation Improvements or additions to documentation needs triage labels Feb 18, 2023
@Badisi
Copy link
Owner

Badisi commented Feb 19, 2023

Hi @PhilippRoessner, thanks for the kind words!

This library clearly lacks documentation.. sorry about that.
But I might have some free time next month to update it ;-)

Meanwhile, you can have a look at the getting-started/providers page, where you have all the necessary settings that needs to be set in your IDP.

For mobile to work, you have to allow this two urls:

demo-app://localhost/?oidc-callback=login
demo-app://localhost/?oidc-callback=logout

Where demo-app is the custom url scheme that needs to be set in:

  • the init of this library (ie. mobileScheme settings through initAuth)
  • the AndroidManifest.xml file of capacitor android
  • the Info.plist file of capacitor iOS

You will also have to install these packages:

  • @capacitor/browser, so that you can log in a modal window
  • @capacitor/preferences (or capacitor-secure-storage-plugin - more secure), so that your tokens can be stored locally

Do not hesitate to have a look at the capacitor demo-app.


The warning is about a work in progress and can be safely ignored right now.
Long story short, the @capacitor/browser is using the SFSafariViewController implementation on iOS which do not share cookies with Safari (results is that SSO cannot work). So I'm planning to release a Capacitor plugin that implements more recents implementations. You can read more about this in this article.

@Badisi Badisi changed the title [DOCS] can't "follow the recommended guide" [DOC] Missing mobile guidelines Feb 19, 2023
@Badisi Badisi added enhancement New feature or request and removed needs triage labels Feb 19, 2023
@PhilippRoessner
Copy link
Author

I followed the guidelines so far (for Android) and I compared with what I've found in the demo application. Unfortunately the Mobile implementation is not working yet. The Browser opens and after the login it goes back to my app. But it seems like the app is not picking up the token etc.

I found this in the log:

(index):243 native SecureStoragePlugin.get (#95697034)
(index):250 ObjectcallbackId: "95697034"methodName: "get"options: {key: 'oidc.user:https://login-dev.-----.com:WebApp2'}pluginId: "SecureStoragePlugin"[[Prototype]]: Object
(index):217 result SecureStoragePlugin.get (#95697034)
(index):225 {message: 'Item with given key does not exist'}

I am subscribing to a few observables of the AuthService (userSession$, accessToken$) but they never fire.

The Webapp with Angular is working fine. Do you have an idea what I could be missing?

@Badisi
Copy link
Owner

Badisi commented Feb 21, 2023

You can enable the log within the library like this:

import { initAuth, Log } from '@badisi/ngx-auth';

initAuth({
    logLevel: Log.DEBUG
});

Then open a Chrome browser and type the following address: chrome://inspect/#devices.
Once there you should be able to see your device/simulator in the list and an inspect button.

Have a look at those logs (especially the ones from [MobileWindow]).
Like [MobileWindow] navigate: successful response: XXX.

You can also copy/paste them here so I can have a look.

@PhilippRoessner
Copy link
Author

PhilippRoessner commented Feb 21, 2023

Sorry for the bad format, here is the log: (interesting stuff seems to be at the end of the log)

Logs
[UserManager] getUser: user not found in storage
/favicon.ico:1          Failed to load resource: the server responded with a status of 404 (Not Found)
/favicon.ico:1          Failed to load resource: the server responded with a status of 404 (Not Found)
main.a24512a315ae2891.js:1 [@badisi/auth-js] This application is currently using a non recommended browser plugin.Please follow the recommended guide and use `@badisi/capacitor-browsertab` instead.
(anonymous) @ main.a24512a315ae2891.js:1
prepare @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
t @ polyfills.b22312040db39e45.js:1
Z @ main.a24512a315ae2891.js:1
signinMobile @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
t @ polyfills.b22312040db39e45.js:1
Z @ main.a24512a315ae2891.js:1
login @ main.a24512a315ae2891.js:1
login @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
n.subscribe.s @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
_subscribe @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
_subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
p @ main.a24512a315ae2891.js:1
h @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
rC @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
y0 @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
y0 @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
n.subscribe.s @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
p @ main.a24512a315ae2891.js:1
h @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
rC @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
p @ main.a24512a315ae2891.js:1
h @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
rC @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
p @ main.a24512a315ae2891.js:1
h @ main.a24512a315ae2891.js:1
_next @ main.a24512a315ae2891.js:1
next @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_complete @ main.a24512a315ae2891.js:1
complete @ main.a24512a315ae2891.js:1
_complete @ main.a24512a315ae2891.js:1
complete @ main.a24512a315ae2891.js:1
_complete @ main.a24512a315ae2891.js:1
complete @ main.a24512a315ae2891.js:1
f @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_complete @ main.a24512a315ae2891.js:1
complete @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
_trySubscribe @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
w @ main.a24512a315ae2891.js:1
subscribe @ main.a24512a315ae2891.js:1
(index):243 native Browser.addListener (#11860707)
(index):243 native Browser.open (#11860708)
(index):217 result Browser.open (#11860708)
(index):217 result Browser.addListener (#11860707)
main.a24512a315ae2891.js:1 [MobileWindow] navigate: error response: Capacitor browser closed by user
error @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
t @ polyfills.b22312040db39e45.js:1
Z @ main.a24512a315ae2891.js:1
onError @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
returnResult @ (index):761
win.androidBridge.onmessage @ (index):731
(index):243 native Browser.removeListener (#11860709)
(index):243 native Browser.close (#11860710)
(index):217 result Browser.close (#11860710)
main.a24512a315ae2891.js:1 [MobileWindow] close: Error: not implemented
    at returnResult ((index):756:32)
    at win.androidBridge.onmessage ((index):731:21)
error @ main.a24512a315ae2891.js:1
(anonymous) @ main.a24512a315ae2891.js:1
invoke @ polyfills.b22312040db39e45.js:1
run @ polyfills.b22312040db39e45.js:1
(anonymous) @ polyfills.b22312040db39e45.js:1
invokeTask @ polyfills.b22312040db39e45.js:1
runTask @ polyfills.b22312040db39e45.js:1
_ @ polyfills.b22312040db39e45.js:1
Promise.then (async)
q @ polyfills.b22312040db39e45.js:1
R @ polyfills.b22312040db39e45.js:1
scheduleTask @ polyfills.b22312040db39e45.js:1
scheduleTask @ polyfills.b22312040db39e45.js:1
scheduleMicroTask @ polyfills.b22312040db39e45.js:1
ee @ polyfills.b22312040db39e45.js:1
z @ polyfills.b22312040db39e45.js:1
(anonymous) @ polyfills.b22312040db39e45.js:1
(anonymous) @ polyfills.b22312040db39e45.js:1
returnResult @ (index):773
win.androidBridge.onmessage @ (index):731
(index):243 native Browser.close (#11860711)
(index):217 result Browser.close (#11860711)
main.a24512a315ae2891.js:1 [MobileWindow] close: Error: not implemented
    at returnResult ((index):756:32)
    at win.androidBridge.onmessage ((index):731:21)

@Badisi
Copy link
Owner

Badisi commented Feb 21, 2023

Sorry but those logs are not helping.. (nothing important in there).

Also they are a lot missing. Look at what I have on my side (clearly more info about what's going on in the lib):
Screen Shot 2023-02-21 at 20 50 25

Two more things:

  1. Have you tried on iOS ?
  2. Have you tried running the demo-app capacitor android from this repo ?

@PhilippRoessner
Copy link
Author

I gave it another shot with the demo-app and it worked perfectly. Then I began to compare everything and noticed that the package @capacitor/app was missing in my project.

It works now perfectly for android. Big Thanks and again Kudos for this project! This is was my expectation on how to implement oAuth into my hybrid app 3 years ago.

My Macbook is currently not ready for development but my colleague is going to try the ios part in the next few days. I'm sure it should be fine :-)

@PhilippRoessner
Copy link
Author

I just tried it now with ios (simulator) but unfortunately the redirect is not working. This what I see after I try to login:

Simulator Screen Shot - iPhone 8 - 2023-02-24 at 08 17 13

@Badisi
Copy link
Owner

Badisi commented Feb 24, 2023

Glad you got it working on Android 😉
Regarding your issue on iOS, I can't help with just a screenshot.. Have you tried the demo-app too ?

@PhilippRoessner
Copy link
Author

I just fixed it now, forgot the url scheme in ios (not enough coffee yet lol)

	<key>CFBundleURLTypes</key>
	<array>
	<dict>
		<key>CFBundleURLName</key>
		<string>com.getcapacitor.capacitor</string>
		<key>CFBundleURLSchemes</key>
		<array>
		<string>url scheme goes here</string>
		</array>
	</dict>

@Badisi
Copy link
Owner

Badisi commented Feb 24, 2023

Where demo-app is the custom url scheme that needs to be set in:

  • the init of this library (ie. mobileScheme settings through initAuth)
  • the AndroidManifest.xml file of capacitor android
  • the Info.plist file of capacitor iOS

Yes it was part of what I told you to do ☕ 😅

@Badisi
Copy link
Owner

Badisi commented Feb 24, 2023

Anyway, enjoy the lib now and thanks again for the support 😊

@ms-emp
Copy link
Contributor

ms-emp commented May 30, 2023

Hi,

First I would like to thank you so much for this library, that's exactly what I was looking for, Keep up the great work!

What's about these errors on android?

image

@Badisi
Copy link
Owner

Badisi commented May 31, 2023

Hello @mordechai-s! Thanks for the kind words 😊

  1. "navigate: error response: Capacitor browser closed by user"
    This error means that the authentication process did not complete. The reason being that the user decided to manually close the authentication window instead of login in.

  2. "close: Error: not implemented"
    This error is thrown by Capacitor itself. Capacitor has a window.close() api on iOS but not yet implemented on Android.
    There is still an opened issue regarding this bug here: feat: provide a workaround for Browser.close() on android ionic-team/capacitor-plugins#50
    In the library I'm using this api to programmatically close the authentication window when the authentication completes. But I think that in this scenario it was not an issue on Android as the window was closed by itself.

Are you facing any issue with these errors ?
Maybe I could silent the second one on Android.. (at least until the PR is merged).

@ms-emp
Copy link
Contributor

ms-emp commented Jun 9, 2023

@Badisi Thanks for your response.

No, I'm not facing any issues with these errors, I just wanted to know what they are.

2 more questions,

  1. How does silent renew work on mobile, does it use an iframe, or it uses a refresh token?
  2. Does the token get removed from secure storage when the user closes the app? how can I do that the user should get logged out when closing the app?

Thanks again

@Badisi
Copy link
Owner

Badisi commented Jun 9, 2023

@mordechai-s

Silent renew is always made with refresh token (except when the app starts on desktop where the iFrame is used instead).

Explanation

On desktop
As a good security practice, tokens are not stored on desktop - they are kept in the app memory.
So as long as you are logged in and the app is live, when your access token is going to expired, the refresh token will be used to renew the user access. But once the app is closed, the tokens are lost.
Reopening the app (or simply refreshing the page), means that the user will not be re-logged in automatically (even if his session is still opened).
So in case the lib was configured with retrieveUserSession: true -> an hidden iFrame will be used to contact the IDP and retrieve the tokens when the app starts.

On mobile
Tokens are stored in the device so that the user could be automatically re-logged in when reopening the app.
Depending on the plugins available in your app, the storage will be (by priority order):

  1. capacitor-secure-storage-plugin
  2. @capacitor/preferences
  3. @capacitor/storage
  4. localStorage

Regarding your question 2.,
a. If your concern is getting sure that the tokens are removed then it is a matter of knowing when the app is getting closed and removing them (maybe using a destroy handler available in your dev framework or listening to an appClose event somewhere ?). But I'm not aware of such a feature in Capacitor so I'm not sure if that would be possible.
b. If your concern is just making sure that the user would not be relogged in automatically then you can simply set retrieveUserSession to false.

@ms-emp
Copy link
Contributor

ms-emp commented Jul 24, 2023

@Badisi

Does the library use the system browser or an in-app browser like Custom Tabs on Android for login? it looks like the user is leaving the app when login, is that the way it is supposed to work?

@Badisi
Copy link
Owner

Badisi commented Jul 26, 2023

This library will never use the "system browser" as it is not considered a best practice.
It is not secured and not even accepted by Apple while doing authentication.

Right now the library is expecting @capacitor/browser (which is an "in-app browser") to be installed. But I'm also planning to develop another library (later on) which will provide "custom tabs / browser tabs browsers".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants