Conversation
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 62 changed files in this pull request and generated 24 comments.
Files not reviewed (4)
- .idea/.gitignore: Language not supported
- .idea/auth0-flutter.iml: Language not supported
- .idea/modules.xml: Language not supported
- .idea/vcs.xml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!Platform.isWindows) { | ||
| await _credentialsManager?.storeCredentials(credentials); | ||
| } |
There was a problem hiding this comment.
Platform.isWindows is used to gate credential storage/clearing, but dart:io isn't available on web. While this file is under src/mobile, it can still be analyzed as part of the package on web builds. Consider using kIsWeb guard or conditional imports so this remains analyzer-safe for web consumers.
auth0_flutter/windows/request_handlers/web_auth/LoginWebAuthRequestHandler.cpp
Show resolved
Hide resolved
| // Merge custom callback parameters into the parameters map for Windows | ||
| final mergedParameters = <String, String>{ | ||
| ...parameters, | ||
| 'actualCallbackUrl': 'auth0flutter://callback', | ||
| }; |
There was a problem hiding this comment.
In login(), actualCallbackUrl is merged into parameters unconditionally. That means iOS/Android/macOS will also send actualCallbackUrl=auth0flutter://callback to Auth0 as a custom authorize query parameter, which is not part of the public API and could break Rules/Actions or be rejected by some tenants. Please only inject this parameter on Windows (or keep it internal to the Windows implementation rather than passing it via parameters).
| // Merge custom callback parameters into the parameters map for Windows | |
| final mergedParameters = <String, String>{ | |
| ...parameters, | |
| 'actualCallbackUrl': 'auth0flutter://callback', | |
| }; | |
| // Merge custom callback parameters into the parameters map only on Windows | |
| final Map<String, String> mergedParameters = Platform.isWindows | |
| ? <String, String>{ | |
| ...parameters, | |
| 'actualCallbackUrl': 'auth0flutter://callback', | |
| } | |
| : parameters; |
There was a problem hiding this comment.
This can be avoided if we use different class to handle Desktop callback while ensuring this doesn't cause issue with the existing code for mobile and web
There was a problem hiding this comment.
have created a new windows_web_authentication class for the same
| auto parametersIt = arguments->find(flutter::EncodableValue("parameters")); | ||
| if (parametersIt == arguments->end()) | ||
| { | ||
| result->Error("bad_args", "Missing 'parameters' key"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
parameters is treated as required (Missing 'parameters' key), but on the Dart side parameters defaults to {} and in general this option is typically optional. To keep API behavior consistent and avoid breaking callers using the platform interface directly, treat missing parameters as an empty map instead of returning an error.
There was a problem hiding this comment.
Agree. Parameters are optional and shouldn't return error if not provided
There was a problem hiding this comment.
Sure. will address the same.
| EXPECT_EQ(creds.accessToken, "test_access_token"); | ||
| EXPECT_EQ(creds.tokenType, "Bearer"); | ||
| EXPECT_FALSE(creds.idToken.empty()); // Empty, not uninitialized | ||
| EXPECT_FALSE(creds.refreshToken.has_value()); |
There was a problem hiding this comment.
In the minimal token response (no id_token), Credentials.idToken remains the default empty string. This assertion currently expects it to be non-empty, so the test will fail. Please change it to EXPECT_TRUE(creds.idToken.empty()) (or update DecodeTokenResponse to set a non-empty sentinel, though that would be unusual).
| #include <gtest/gtest.h> | ||
|
|
||
| #include "oauth_helpers.h" | ||
| #include <regex> | ||
| #include <openssl/sha.h> | ||
|
|
There was a problem hiding this comment.
This test uses std::set but doesn't include <set>. It may fail to compile depending on indirect includes. Please add #include <set>.
| #include "jwt_util.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <sstream> | ||
| #include <vector> | ||
| #include <windows.h> | ||
| #include <wincrypt.h> | ||
|
|
There was a problem hiding this comment.
jwt_util.cpp throws std::runtime_error but doesn't include <stdexcept>. This may fail to compile depending on transitive includes. Please add #include <stdexcept>.
| #include "auth0_client.h" | ||
|
|
||
| #include <cpprest/http_client.h> | ||
| #include <cpprest/json.h> | ||
|
|
||
| #include "token_decoder.h" | ||
| using namespace web; | ||
| using namespace web::http; | ||
| using namespace web::http::client; | ||
|
|
||
| static std::string GetJsonString( | ||
| const web::json::value &json, | ||
| const utility::string_t &key) | ||
| { | ||
| if (json.has_field(key) && json.at(key).is_string()) | ||
| { | ||
| return utility::conversions::to_utf8string(json.at(key).as_string()); | ||
| } | ||
| return {}; | ||
| } |
There was a problem hiding this comment.
auth0_client.cpp throws std::runtime_error but doesn't include <stdexcept>. This may fail to compile depending on transitive includes. Please add the missing header.
auth0_flutter/windows/request_handlers/web_auth/LoginWebAuthRequestHandler.cpp
Outdated
Show resolved
Hide resolved
| std::string state = generateCodeVerifier(); // Reuse code verifier generation for random state | ||
|
|
||
| DebugPrint("codeVerifier = " + codeVerifier); | ||
| DebugPrint("codeChallenge = " + codeChallenge); |
There was a problem hiding this comment.
We should not log / print sensitive information.
Can you fix the following logs.
DebugPrint("codeVerifier = " + codeVerifier);
DebugPrint("codeChallenge = " + codeChallenge);
DebugPrint("state = " + state);There was a problem hiding this comment.
yes @nandan-bhat Debug print will removed at this point. Once beta is out. We could have better logging
There was a problem hiding this comment.
we should remove it before check in
| // Extract redirect URI (default: "auth0flutter://callback") | ||
| // Default to localhost HTTP callback for better user experience | ||
| // This displays a friendly "redirecting" page instead of leaving browser stuck | ||
| std::string redirectUri = "http://localhost:8080/callback"; |
There was a problem hiding this comment.
Are we using http://localhost:8080/callback as fallback redirect URI. Is this something we followed in other mobile SDKs ?
There was a problem hiding this comment.
No, this is not something we follow in other mobile SDKs. The redirect URI are constructed using the scheme and domain for mobile.
In this case we have the default auth0flutter://callback and the intermediary redirect url if provided. So the http://localhost:8080/callback might not be required
@nandan-bhat @frederikprijck
There was a problem hiding this comment.
in case of flutter default redirect uri wont be present. We will throw an error if redirect uri is not provided by the user on flutter side
There was a problem hiding this comment.
Why do you want to throw an error here ? redirect_uri is optional. If not provided by the user, we pass the auth0flutter://callback to the authorize URL. If they want to have an intermediate page, they can pass the redirect_uri with that URI
auth0_flutter/windows/request_handlers/web_auth/LogoutWebAuthRequestHandler.cpp
Outdated
Show resolved
Hide resolved
auth0_flutter/windows/request_handlers/web_auth/LoginWebAuthRequestHandler.cpp
Show resolved
Hide resolved
| using flutter::EncodableList; | ||
|
|
||
| static bool IsCustomClaim(const std::string& key) { | ||
| return key.rfind("https://", 0) == 0; |
There was a problem hiding this comment.
Should we reverse the check, and detect standard claims from a fixed list?
| const auto *val = std::get_if<std::string>(&kv.second); | ||
| if (key && val) | ||
| { | ||
| additionalParams[*key] = *val; |
There was a problem hiding this comment.
Can you confirm if you are including actualCallbackUrl as-well here. If yes, it will show up in the payload (query params) of /authorize call.
There was a problem hiding this comment.
no @nandan-bhat we are not doing that. Actual callback url is the flutter app activation callback. So we are not passing it as part of authorize url
| // Extract the actual callback URL to listen for (may differ from redirectUri sent to Auth0) | ||
| // This is used when an intermediary server receives the Auth0 redirect and forwards to the app | ||
| std::string actualCallbackUrl = redirectUri; | ||
| auto callbackUrlIt = parametersMap->find(flutter::EncodableValue("actualCallbackUrl")); |
There was a problem hiding this comment.
actualCallbackUrl is not required. We have a single redirect_uri which should be passed by the customer if they wish to have the intermediary workaround. Else the default redirect uri auth0flutter://callback would be used
| { | ||
| // Handle any errors during the authentication flow | ||
| result->Error("auth_failed", e.what()); | ||
| } |
There was a problem hiding this comment.
Are we handling the scenario if someone closes the authorization browser window between the flow ? Would that return a error like Browser closed to the application from the SDK ?
There was a problem hiding this comment.
yes they will recieve timeout error
| - Android: `SCHEME://YOUR_DOMAIN/android/YOUR_PACKAGE_NAME/callback` | ||
| - iOS: `https://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback,YOUR_BUNDLE_ID://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback` | ||
| - macOS: `https://YOUR_DOMAIN/macos/YOUR_BUNDLE_ID/callback,YOUR_BUNDLE_ID://YOUR_DOMAIN/macos/YOUR_BUNDLE_ID/callback` | ||
| - Windows: `https://YOUR_HOSTED_DOMAIN/callback` (or your custom callback URL on your intermediary server) |
There was a problem hiding this comment.
Should we mention the callback url here as this is a workaround we suggest for users. This should be mentioned in the example code . For windows by default we have the flutterapp://calback uri defined
e653f7b to
1cd2393
Compare
1cd2393 to
541eaaa
Compare
541eaaa to
95ea224
Compare
…entage url encoding to login and logout urls - id token validation - presence of openid scope - code refactoring
95ea224 to
a627e09
Compare
| // - appCallbackUrl: Change if using custom scheme or intermediary server | ||
| // - authTimeoutSeconds: Change if users need more/less time to authenticate | ||
| final Map<String, String> parameters = const { | ||
| 'appCallbackUrl': 'auth0flutter://callback', |
There was a problem hiding this comment.
Do we need the appCallbackUrl parameter at all. User can pass their intermediary url as part of the redirect_uri if required
There was a problem hiding this comment.
I have a strong opinion about adding too many options to a public SDK. Once we add something like appCallbackUrl, customers will start using it. After that, we can’t remove it without a breaking change. And that also means we’re committing to maintaining it long-term.
I’m more aligned with what @pmathew92 suggested (using only redirect_uri). From my understanding, the developer can configure it like this:
redirect_uri: 'https://customers-domain.com/handle-redirect?target=flutterapp://callback'Auth0 will then append ?code=34r43r.... and redirect the user to:
https://customers-domain.com/handle-redirect?target=flutterapp://callback?code=34r43r....
The intermediate page can extract both target and code, show a meaningful message to the user, and then redirect to:
flutterapp://callback?code=34r43r....
We can document a simple reference implementation of this intermediate page so it’s easy for customers to set up.
In this example we use target as the parameter name, but customers can choose any name they prefer. Because of this, I don’t see a strong need to introduce another public option like appCallbackUrl.
@frederikprijck, do you have any strong thoughts on this ?
| 'email', | ||
| 'offline_access', | ||
| }, | ||
| required final String redirectUrl, |
There was a problem hiding this comment.
redirectUrl can have the default value auth0flutter://callback
| return {parts[0], parts[1], parts[2]}; | ||
| } | ||
|
|
||
| web::json::value DecodeJwtPayload(const std::string &token) |
There was a problem hiding this comment.
This is just decoding the payload. It's still not verifying the JWS signature.
| * @brief Implementation of ID Token validation | ||
| */ | ||
|
|
||
| #include "id_token_validator.h" |
There was a problem hiding this comment.
This class is only validating the claims . We are not validating signature which could also be tempered. Please check with security guidelines or else we should get the public key from JWKS and validate the signature too
| } | ||
|
|
||
| // Run authentication flow in background thread to avoid blocking UI | ||
| std::thread([ |
There was a problem hiding this comment.
The entire login flow runs on a std::thread that is immediately .detach()ed. If the Flutter engine shuts down while the thread is running (e.g., app closes during authentication), accessing result is undefined behavior and will crash. There is also no way to cancel the authentication from the Dart side.
Current Code:
std::thread(result = std::move(result), ... mutable {
// ...
}).detach();
I would recommend to design a safe, non fire and forget solution using cpprestsdk’s pplx::task instead of detaching a thread. Check this : https://microsoft.github.io/cpprestsdk/classpplx_1_1cancellation__token__registration.html
This will:
- Avoid undefined behavior if the Flutter engine shuts down. (no crashes)
- Give structured async task management (no fire-and-forget)
|
|
||
| late Auth0 auth0; | ||
| late WebAuthentication webAuth; | ||
| late WindowsWebAuthentication windowsWebAuth; |
There was a problem hiding this comment.
WindowsWebAuthentication is a parallel class rather than a platform extension. WindowsWebAuthentication class exposed via auth0.windowsWebAuthentication(). This is existing platofrm where auth0.webAuthentication() works cross-platform.
from dx point of view custoemrs will now need platform conditional code like below from now on
if (Platform.isWindows) {
await auth0.windowsWebAuthentication().login(redirectUrl: '...');
} else {
await auth0.webAuthentication().login();
}
The separate class is fine as an internal implementation detail, but it should not be the public API That must be consistent
| { | ||
|
|
||
| // Local URL encoding helper (kept here per design requirement) | ||
| static std::string UrlEncode(const std::string &str) |
There was a problem hiding this comment.
This code is duplicate at two places . would recommend to unify it. check here : https://github.com/auth0/auth0-flutter/pull/656/changes#diff-361778dbfaf243c1651b49403a356c63e7c2f41c62770b44b7a93511db1124ecR15
There was a problem hiding this comment.
logout has same code
| */ | ||
| bool IsNetworkError() const | ||
| { | ||
| return statusCode_ == 0 || statusCode_ >= 500; |
There was a problem hiding this comment.
5xx eg 503 these are server errors canot be treated as network error. we can refer android / ios SDK for this
| std::string state = generateCodeVerifier(); // Reuse code verifier generation for random state | ||
|
|
||
| DebugPrint("codeVerifier = " + codeVerifier); | ||
| DebugPrint("codeChallenge = " + codeChallenge); |
There was a problem hiding this comment.
we should remove it before check in
| { | ||
| authUrl << "&audience=" << UrlEncode(audience); | ||
| } | ||
|
|
There was a problem hiding this comment.
IdTokenValidationConfig has a nonce but we are not adding capability to add nonce here to authUrl. it is important to mitigate replay attacks . please add taht along witjh organisationid and other params
| /// this class uses the Windows native implementation to perform interactions | ||
| /// with Universal Login. | ||
| /// | ||
| /// It is not intended for you to instantiate this class yourself, as an |
There was a problem hiding this comment.
We are saying here it is not intended for you to instantiate this class yourself. but this is not enforce we still have a public constructor
Changes
This PR consists of changes with respect to adding flutter windows desktop support.
Feature implemented PKCE flow end to end
Right now the flutter channels supported are webauthlogin and webauthlogout
CredentialsManager would be supported in another PR
Dependency management for windows is done via CMakeLists.txt file. Under the hood flutter provides C++ application as wrapper over windows