Open JonSilver opened 3 years ago
In case it helps anyone else, here's my solution which temporarily persists the query string to local storage and then recovers it when the app is reloaded. I call loginWithSavedState
to allow the user to login, and recoverSavedState
from a useEffect
hook in my App.js
.
const SAVED_STATE_KEY = "MyAppSavedState";
export const prepareLoginUrl = () => {
const redirectUrl = `${window.location.origin}${window.location.pathname}`;
return `/.auth/login/aad?post_login_redirect_uri=${redirectUrl}`;
};
export const loginWithSavedState = () => {
localStorage.setItem(SAVED_STATE_KEY, window.location.search);
window.location.assign(prepareLoginUrl());
};
export const recoverSavedState = () => {
const queryString = localStorage.getItem(SAVED_STATE_KEY);
if (queryString) {
window.location.assign(`${window.location.href}${queryString}`);
localStorage.removeItem(SAVED_STATE_KEY);
}
};
So I can set an href
to a simple login URL from prepareLoginUrl()
, or execute a login with saved state using loginWithSavedState()
.
Then in my App
component:
useEffect(() => {
recoverSavedState();
}, []);
It'd be nice if the platform took care of all that for us though. There are precedents elsewhere.
Of course I'm just using this to take some initialisation state contained in the query string parameters and persist those through the login process. You could use the same technique to persist any sort of state content through the destructive cross-site, round-tripping login process that effectively relaunches your client side app in an uninitialised state.
Post_login_redirect_uri and post_logout_redirect_uri should now support query parameters. Make sure to encode them before appending to the route rule if using a redirect. For example:
{
"route": "/login",
"redirect": "/.auth/login/aad?post_login_redirect_uri=%2findex.html%3fq1%3dp1%26q2%3dp2"
},
{
"route": "/logout",
"rewrite": "/.auth/logout?post_logout_redirect_uri=/index.html?q1=p1&q2=p2"
}
Post_login_redirect_uri and post_logout_redirect_uri should now support query parameters. Make sure to encode them before appending to the route rule if using a redirect. For example:
{ "route": "/login", "redirect": "/.auth/login/aad?post_login_redirect_uri=%2findex.html%3fq1%3dp1%26q2%3dp2" }, { "route": "/logout", "rewrite": "/.auth/logout?post_logout_redirect_uri=/index.html?q1=p1&q2=p2" }
The parameters needs to be passed from code - in above example - it is hardcoded in staticwebappconfig.json. Atleast with local development (
swa start
v0.8.0), it still does not work.
But this makes no sense, why do we have query parameters? For dynamic values! They are parameters... how do we specify that the dynamic value should be used within staticwebappconfig.json
? For example something like this would be nice:
{
"route": "/login",
"redirect": "/.auth/login/aad?post_login_redirect_uri=/index.html?q1={q1}&q2={q2}"
},
No problem if the special characters need to be encoded, but this for clarity...
Heya,
I've just bumped on this too. I thought I'd share my use case. Consider the following URL:
A support engineer is using my.app and finds something of interest, they send the above URL to their colleague. The colleague clicks on the link, expecting to see what their colleague sees. Instead, they are authenticated and then presented with an empty screen.
The configuration is as follows:
"responseOverrides": {
"401": {
"redirect": "/.auth/login/aad?post_login_redirect_uri=.referrer",
"statusCode": 302
}
},
Ideally, the .referrer
would include the full querystring as well as the path. It doesn't at present which impacts our users.
related tangentially: https://github.com/Azure/static-web-apps/issues/785
@JonSilver , just reading up on your workaround. I'm curious as to what your staticwebappconfig.json
looks like. Here's mine:
{
"auth": {
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/6d6a11bc-469a-48df-a548-d3f353ac1be8/v2.0",
"clientIdSettingName": "AAD_CLIENT_ID",
"clientSecretSettingName": "AAD_CLIENT_SECRET"
}
}
}
},
"navigationFallback": {
"rewrite": "index.html"
},
"routes": [
{
"route": "/login",
"rewrite": "/.auth/login/aad",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/.auth/login/github",
"statusCode": 404
},
{
"route": "/.auth/login/twitter",
"statusCode": 404
},
{
"route": "/logout",
"redirect": "/.auth/logout",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/*",
"allowedRoles": ["authenticated"]
}
],
"responseOverrides": {
"401": {
"redirect": "/.auth/login/aad?post_login_redirect_uri=.referrer",
"statusCode": 302
}
},
"globalHeaders": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, OPTIONS",
"content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
},
"mimeTypes": {
".json": "text/json",
".md": "text/markdown",
".xml": "application/xml"
}
}
We force authentication of all users with this:
{
"route": "/*",
"allowedRoles": ["authenticated"]
}
I'm guessing this wouldn't work with your approach as you'd not get the chance to save state to localstorage before the redirect. How do you handle this? Do you not use the above approach? Perhaps you allow unauthenticated access in the app and force authentication in the JavaScript? Or do you something else?
I've taken @JonSilver's approach and run with it. I've come up with a framework agnostic approach to do roughly what Jon suggested. I've blogged about it here and I've created a package to ease consumption called easyauth-deeplink
https://github.com/johnnyreilly/blog.johnnyreilly.com/pull/365
svdoever johnnyreilly JonSilver We now have support for preserving the query string when using post_login_redirect_uri=.referrer
like so:
"responseOverrides": {
"401": {
"redirect": "/.auth/login/<aad or any other provider>?post_login_redirect_uri=.referrer",
"statusCode": 302
}
},
Please let me know if this solves your scenario, and thank you for using Azure Static Web Apps!
Hi @mishapos - thanks I've used that. The problem is it doesn't preserve querystring / search parameters and so it doesn't solve the problem. It would be amazing if post_login_redirect_uri=.referrer
did preserve those values. Is there any chance this behaviour could be added?
Hi @johnnyreilly, sorry, I should have been clearer. What I am saying is that we have rolled out an update and the behavior you are describing should now be available. The query string should now be preserved after the login flow.
Oh wow! When did that get rolled out? I'd be happy to give it a go - my workaround seemed to be necessary up to two weeks ago based on systems that I work on. Do you need a particular ARM/bicep template version for it to work!
It finished just this week. No ARM/bicep template needed. It should just work for existing apps.
Oh cool! I'll be off work until new year but I'll give it a try then. If it works I'll update my blog post to reflect!
Apologies for the delay in reporting back @mishapos - it doesn't look like the .referrer
functionality works. Let me share my findings:
When the following staticwebapp.config.json
is in play:
{
"auth": {
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/6d6a11bc-469a-48df-a548-d3f353ac1be8/v2.0",
"clientIdSettingName": "AAD_CLIENT_ID",
"clientSecretSettingName": "AAD_CLIENT_SECRET"
}
}
}
},
"navigationFallback": {
"rewrite": "index.html"
},
"routes": [
{
"route": "/login",
"rewrite": "/.auth/login/aad?post_login_redirect_uri=.referrer",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/.auth/login/github",
"statusCode": 404
},
{
"route": "/.auth/login/twitter",
"statusCode": 404
},
{
"route": "/logout",
"redirect": "/.auth/logout",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/*",
"allowedRoles": ["authenticated"]
}
],
"responseOverrides": {
"401": {
"redirect": "/login",
"statusCode": 302
}
},
"globalHeaders": {
"content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
},
"mimeTypes": {
".json": "text/json",
".md": "text/markdown",
".xml": "application/xml"
}
}
The .referrer
value didn't seem to trigger the desired behaviour:
Am I doing anything wrong here? Would be happy to jump on a call and demo this to you.
I've tried again with this staticwebapp.config.json
:
{
"auth": {
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/6d6a11bc-469a-48df-a548-d3f353ac1be8/v2.0",
"clientIdSettingName": "AAD_CLIENT_ID",
"clientSecretSettingName": "AAD_CLIENT_SECRET"
}
}
}
},
"navigationFallback": {
"rewrite": "index.html"
},
"routes": [
{
"route": "/login",
"rewrite": "/.auth/login/aad",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/.auth/login/github",
"statusCode": 404
},
{
"route": "/.auth/login/twitter",
"statusCode": 404
},
{
"route": "/logout",
"redirect": "/.auth/logout",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/*",
"allowedRoles": ["authenticated"]
}
],
"responseOverrides": {
"401": {
"redirect": "/.auth/login/aad?post_login_redirect_uri=.referrer",
"statusCode": 302
}
},
"globalHeaders": {
"content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
},
"mimeTypes": {
".json": "text/json",
".md": "text/markdown",
".xml": "application/xml"
}
}
And it seems to work! I guess the caveat is that it has to be the upfront redirect
; rewrite
won't handle this for you.
@johnnyreilly I came across this thread since I run in the very same issue. I have a deep link on a SharePoint page pointing to my static web app.
I have set the following part as well:
"responseOverrides": { "401": { "redirect": "/.auth/login/aad?post_login_redirect_uri=.referrer", "statusCode": 302 } }
But after authenticating I am never redirected to the deep link but to the main page instead.
Any more hints why this occurs?
Why doesn't .referrer get populated when redirect to urls that do not start with "./auth/login/
post_login_redirect_uri
does not pass on query string parametersstate
parameter supported by some other Azure auth implementations appears to be unsupported in Azure Static Web Apps, and indeed isn't documented anywhereSo I guess I'm going to have to do something elaborate to save my own state to local storage or cookie before the login is performed, and recover it afterwards?
Please confirm the expected implementation pattern for this scenario.
Thanks!