inrupt / solid-client-authn-js

A client library for authenticating with Solid
https://solid-client-authn-js.vercel.app
Other
69 stars 42 forks source link

Browser/App: Restore session without silent login and redirect #3443

Open Maximvdw opened 7 months ago

Maximvdw commented 7 months ago

Hey,

In NodeJS there is the possibility for an application to request a refresh token that can be stored on the server. I am developing a hybrid mobile application that will run in a secure context and I use the browser authentication to do so.

Everything works fine, I have a clientId: https://sembeacon.org/id.jsonld that could request a refresh_token, etc... However, I want to disable the silent login that redirects the user away to the Authorization server whenever they reopen the app, since other than a browser - this is a much more visible process in a mobile application. I already have my insecure and secure storage configured for localStorage - but this does not seem to work.

From what I see, the client authentication uses the following authentication flow: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth while the node implementation has a refresh token flow. There does not seem to be a possibility to change this flow since the login flow is defined in: https://github.com/inrupt/solid-client-authn-js/blob/4902d206dcd6dea3d0fcf9393f9363d644c5709d/packages/browser/src/dependencies.ts#L81-L86 which can not be changed or configured before it is loaded by a session.

Is it possible to somehow to either: 1) Request a refresh token in the browser 2) Retrieve and reuse the id_token and access_token to restore the session when the access token has not expired yet? 3) My next attempt will be to override the clientAuthentication object in each session, to replace the login flow with something that has the RefreshTokenOidcHandler in an AggregateOidcHandler. However, I do not know if this will work and it also feels like a solution for an issue that should not be there. A configuration to use refresh tokens would be useful or at least a solution to persist the access token and id_token for page refresh.

Best, Maxim

elf-pavlik commented 7 months ago

I think one would have to keep in mind https://www.ietf.org/archive/id/draft-ietf-oauth-security-topics-25.html#name-refresh-token-protection Other related factors are possible, including the possibility of other apps sharing the exact origin and being able to access stored refresh tokens.

Maximvdw commented 7 months ago

In the case of hybrid apps I would argue that storing refresh tokens is useful and secure.

But the issue that the access tokens can not be stored is quite problematic.

NSeydoux commented 6 months ago

Hi @Maximvdw, and thanks for reaching out .

You should already be able to get a refresh token in the browser, as it is currently the mechanism used to extend an open session beyond the (purposefully short) lifetime of the issued Access Token. If you log in using @inrupt/solid-client-authn-browser, you will see regular token refresh requests going to the OpenID Provider over time.

In @inrupt/solid-client-authn-node, you can use a previously obtained refresh token to log a session back in, when used in combination with client identifiers (either Client Credentials or a Solid-OIDC Client Identifier). However, due to the refresh token being a sensitive piece of data that may be used to get a valid Access Token, it has to be used with care, and in particular our threat model is based on the assumption that there is no secure permanent storage in the browser, because it is shared by origin so a malicious script loaded from the same origin as your application could look into localStorage to get your refresh token and get access to user data.

For this reason, the @inrupt/solid-client-authn-browser library only supports Refresh Tokens to keep a session live, and not to log back in an expired session for a returning user. Persisting either the refresh token or the ID token in browser storage is not something we encourage or plan on supporting. The canonical login flow for in-browser clients is the authorization code flow, which involves a redirect. We plan to add support for popup login support in the future: would a popup-based redirect, rather than a full-window one, be more acceptable in your use case?

Maximvdw commented 6 months ago

For this reason, the @inrupt/solid-client-authn-browser library only supports Refresh Tokens to keep a session live, and not to log back in an expired session for a returning user. Persisting either the refresh token or the ID token in browser storage is not something we encourage or plan on supporting. The canonical login flow for in-browser clients is the authorization code flow, which involves a redirect. We plan to add support for popup login support in the future: would a popup-based redirect, rather than a full-window one, be more acceptable in your use case?

In a Capacitor app - both will break the UX flow of the application. I understand your concern with localStorage. But for a mobile application using a webview, this security risk does not exist. In addition, browser sessions currently support the insecureStorage and secureStorage options - it would be logical for developers to assume that a secureStorage session can indeed be.. secure. In a hybrid app this could even be an encrypted storage, etc... . I know that in node these two options are deprecated, but while they are available in the browser I would assume they serve a purpose.

So removing the feature of storing/accessing refresh tokens simply because the assumption is that secureStorage is not secure (thinking only in the context of the Web instead of the 'browser' like the library name suggests) is not a developer friendly API.

As an FYI: in capacitor I use https://github.com/martinkasa/capacitor-secure-storage-plugin to secure the storage of sessions.

Currently I had to perform a large workaround where the complete "handleRedirect" has to be rewritten, since the API offers no other way to intervene in the storage of access tokens (since they are used to create a fetch method and are then disposed). As for the refresh tokens, this is currently not (yet) handled. In additon, most internal API's are not exported so doing this workaround is in fact not easy. https://github.com/OpenHPS/openhps-solid/blob/master/src/browser/SolidClientService.ts#L158-L285

TL;DR: In a sense, a hybrid 'browser' application is similar to a self-contained and secure nodejs application. Other than a 'browser' application that runs on the Web with the security concerns involved. With the use of secureStorage and insecureStorage I would assume developers are aware that they are storing 'secure' and 'insecure' data. Currently no access tokens or refresh tokens are stored in the secureStorage in the browser.