Closed phatmandrake closed 3 years ago
The Enable-PodeSessionMiddleware
is missing a -Duration
:) I did fix all of these at one point, must have slipped through the cracks!
Set the session to the following, and it should be good:
Enable-PodeSessionMiddleware -Duration 120
Okay it refreshes now without error, but I'm not seeing the flash message 🤔
Huh, I just ran the example (with the above change) and get the flash message back 🤔
How does this look 🤔
Start-PodeServer -Browse {
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
Set-PodeViewEngine -Type Pode
# setup session and csrf middleware
Enable-PodeSessionMiddleware -Secret 'vegeta' -Duration 120
Enable-PodeCsrfMiddleware
# this route will work, as GET methods are ignored by CSRF by default
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
$token = (New-PodeCsrfToken)
Write-PodeViewResponse -Path 'index' -Data @{ 'csrfToken' = $token } -FlashMessages
}
# POST route for form which will require the csrf token from above
Add-PodeRoute -Method Post -Path '/token' -ScriptBlock {
Add-PodeFlashMessage -Name 'message' -Message $WebEvent.Data['message']
Move-PodeResponseUrl -Url '/'
}
}
<html>
<head>
<title>CSRF Example Page</title>
</head>
<body>
<h1>Example form using a CSRF token</h1>
<p>Clicking submit will just reload the page with your message</p>
<form action='/token' method='POST'>
<!-- the hidden input for the CSRF token needs to have the name 'pode.csrf' -->
<input type='hidden' name='pode.csrf' value='$($data.csrfToken)' />
<input type='text' name='message' placeholder='Enter any random text' />
<input type='submit' value='Submit' />
</form>
<!-- on the page reload, display your message -->
$(if ($data.flash['message']) {
"<p>$($data.flash['message'])</p>"
})
</body>
</html>
Yup, works fine 👀
Interesting. Test-PodeFlashMessage doesn't seem to recognize the the message exist if I test for it at the default path before the view response. It does after it adds the flash message in the Token route.
Does this imply it may not be tracking the session?
I did this:
Start-PodeServer -Browse {
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http
Set-PodeViewEngine -Type Pode
# setup session and csrf middleware
Enable-PodeSessionMiddleware -Secret 'vegeta' -Duration 120
Enable-PodeCsrfMiddleware
# this route will work, as GET methods are ignored by CSRF by default
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
$token = (New-PodeCsrfToken)
Test-PodeFlashMessage -Name 'message' | Out-Default
Write-PodeViewResponse -Path 'index' -Data @{ 'csrfToken' = $token } -FlashMessages
}
# POST route for form which will require the csrf token from above
Add-PodeRoute -Method Post -Path '/token' -ScriptBlock {
Add-PodeFlashMessage -Name 'message' -Message $WebEvent.Data['message']
Test-PodeFlashMessage -Name 'message' | Out-Default
Move-PodeResponseUrl -Url '/'
}
}
I get False
on the initial page load, but thereafter I get True
from the /token
route and the /
route 🤔
Is the code above similar to what you have with your Test-PodeFlashMessage
or different?
I'm getting False-True-False. It doesn't think there's a flash message after the redirect.
That is strange 🤔 which version of Pode/PowerShell are you using? Because you're right, that does sound like something's preventing it from saving the session's data.
All this is running locally your your machine, I assume?
Local, MacOS, 7.1.4, 2.4.1 All up to date.
I just tried those on Windows and Ubuntu, both worked as expected 🤔 Are you able to try either of those and see what happens?
Do you get anything by adding New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging
as well? Just in case.
Man I am really striking out today.
CentOS 8: (Currently using for REST endpoints). Started another instance of pode, for some reason Pode is unable to find index.pode and returns 404 --- the file is for sure in the current directory that pode is being invoked from. No idea what that's about ....
Windows: Still No flash message. First time install for Pode there as well.
When I ran the server as root (CentOS 8) these errors popped up
This is bizarre! I just tried on a fresh Windows VM and it worked 🤯
The errors from CentOS are probably because there's no browser to be opened - removing -Browser
should stop those.
I guess to be absolutely sure I'm doing everything the same: if you're using the just the example would just be able to attach a zip/7z of the server? If not, could you show a screenshot of the file/folder structure; an example of the code (which I'm guessing is just the example! 😂); and the steps you're taking on the page/form itself?
It's just a single folder and the example. I'm gonna be embarrassed if I forgot a comma somewhere. 😆
First thing I spotted is the index.pode should be in a views folder :) I've updated the docs to fix that it doesn't mention that!
After that, it works for me (on Windows it worked even without the views folder, which I probably need to look into!) 🤔
I'm gonna find another laptop to try this on https://user-images.githubusercontent.com/24230425/130709543-5a214786-8434-4909-8765-6e8397b5edf5.mov
What happens if you take out the CSRF?
Commenting out:
Enable-PodeCsrfMiddleware
$token = (New-PodeCsrfToken)
Start-PodeServer {
Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http
Set-PodeViewEngine -Type Pode
# setup session and csrf middleware
Enable-PodeSessionMiddleware -Secret 'vegeta' -Duration 120
#Enable-PodeCsrfMiddleware
# this route will work, as GET methods are ignored by CSRF by default
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
#$token = (New-PodeCsrfToken)
Test-PodeFlashMessage -Name 'message' | Out-Default
Write-PodeViewResponse -Path 'index' -Data @{ 'csrfToken' = $token } -FlashMessages
}
# POST route for form which will require the csrf token from above
Add-PodeRoute -Method Post -Path '/token' -ScriptBlock {
Add-PodeFlashMessage -Name 'message' -Message $WebEvent.Data['message']
Test-PodeFlashMessage -Name 'message' | Out-Default
Move-PodeResponseUrl -Url '/'
}
}
Does that still exhibit the same problem?
I just tried a different timezone, in case the cookie was being weird, but is you local set to UTC or something else? I'm wondering if the session is being immediately timed out somehow, even though it has a duration of 2mins. If you open dev-tools in the browser, and go to Application > Cookies. If the pode.sid cookie value changes everytime you click "Submit" in the form, the session is being reset each time.
Well the message shows when I comment those lines out. I am using a standard time convention, and the session id is definitely changing each time. How is this value supposed to be stored?
I managed to get a couple other people to try the example, and for them it worked - one of them was also on MacOS as well. Someone suggested what happens if you disable the browser extensions, or try in a different browser?
If commenting out the CSRF lines makes this work, then it suggests that sessions are working ok and it's the CSRF part at fault 🤔 The flash value is written to the current session and saved to Pode's in-memory storage. When a session has data saved, then the pode.sid
cookie is updated with the expiry so it lasts for 2mins. The session middleware gets the cookie, and gets the session data from the in-memory store, and reads the flash message from there. If the session with CSRF keeps changing, then somehow it's stopping it from saving the session properly.
(The in-memory store for session is simply a PSObject and Hashtable).
I have tried on 5 computers with the same results a Windows 10 VM, A Centos 8 VM, A Win Server 2016 VM, MacOS Catalina, my Windows 10 Gaming Desktop. All the same issue.
And I finally got ahold of another Mac - fresh build - Big Sur - Pode 1.4.1 - PS 7.1.4 - still not working.
Edit: It works if I use the -UseCookies
option on Enable-PodeCsrfMiddleware
Maybe you can give me a zip file of you working one.
However:
https://badgerati.github.io/Pode/Tutorials/Middleware/Types/Sessions/
The example at the bottom does not work as is. The server fails to start since no endpoint is specified.
Start-PodeServer {
Enable-PodeSessionMiddleware -Duration 120
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
$WebEvent.Session.Data.Views++
Write-PodeJsonResponse -Value @{ 'Views' = $WebEvent.Session.Data.Views }
}
}
Adjusting it. I can refresh the page once and it will increment to 2
, but then it resets back to 1 on subsequent resets. Shouldn't it increment until the session duration elapses? Also Get-PodeSessionId | Out-Default
doesn't render anything in the following example.
Start-PodeServer {
Enable-PodeSessionMiddleware -Duration 120
Add-PodeEndpoint -Address localhost -Protocol http -Port 8081
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
Get-PodeSessionId | Out-Default
$WebEvent.Session.Data.Views++
Write-PodeJsonResponse -Value @{ 'Views' = $WebEvent.Session.Data.Views }
}
}
It definitely seems like sessions just aren't working. I don't fully understand how sessions work. If sessions are stored server side how does Pode recognize the browser as being the same without using a cookie, or passing it along in the header.
Edit: It is worth mentioning that I am using sessions elsewhere with the -useheaders
flag for a REST api; however, sessions did not work until I had an Authentication scheme attached to a Pode Route. Not sure if its related. I ended doing something like this.
Start-PodeServer {
Enable-PodeSessionMiddleware -Duration 120
$OAuthScheme = New-PodeAuthScheme -Custom -ScriptBlock {
return
}
$OAuthScheme | Add-PodeAuth -Name 'Login' -ScriptBlock {
return @{ User = "True" }
}
Add-PodeEndpoint -Address * -Protocol http -Port 8081
Add-PodeRoute -Authentication 'Login' -Method Get -Path '/' -ScriptBlock {
Get-PodeSessionId | Out-Default
$WebEvent.Session.Data.Views++
Write-PodeJsonResponse -Value @{ 'Views' = $WebEvent.Session.Data.Views }
}
}
Sessions for the REST api did not work with the -useheaders
flag until I added an authentication scheme, but in that scenario, I wanted the sessions to be started unauthenticated so I had to make a custom scheme that just doesn't do anything except produce True
The above example takes the custom scheme from the REST API implementation I have, and is modified to show how now a SessionID is displayed. In previous examples this is not working without an auth scheme attached to a route. It should be noted that the SessionID that is returned is definitely changing and not staying the same, but I'm not at all sure how Pode would track this without passing a sessionID along as a query/form parameter or in the header (which is how it is working for my REST API implementation)
Hey, apologies for the late response here 🙇 - I was finishing up the release over on Pode.Web, but can get back to doing work on Pode now.
Sessions and Headers requires Authentication, (here), the idea for them is so a user can use a REST call to login, supply the session for other requests, then logout. If you've got a scenario for sessions on a REST API with no authentication, I can look into it!
This is where I'm wondering if something strange is happening to the cookie. When a user logs in via the frontend Pode creates and storage a SessionId with data in-mem, and this SessionId is sent back to the browser as a Set-Cookie
header to create the cookie. On subsequent requests from the browser, the browser automatically resends current, non-expired, cookies on all requests. Pode parses these cookies from the request, one hopefully being pode.sid, and uses the value of that to retrieve the Session data.
Now I've released Pode.Web I'll look into this more, one idea I had is what happens if you set the -Duration
to something crazy? Like -Duration 604800
so it expires after a week?
Get-PodeSessionId
only returns the SessionId if the current session is for an authenticated user, but in hindsight it should probably just just return the current SessionId regardless.
If the request doesn't contain a a pode.sid cookie or header, or the SessionId is invalid, then Pode will regenerate a new one each time. The session is only saved and returned as a cookie/header on response if data is set on the session (like Views++
or the authenticated user)
If you've got a scenario for sessions on a REST API with no authentication, I can look into it.
Well there's an easy way around it so probably not worth the effort to do anything specific about it now at least. 😄
if you set the -Duration to something crazy? Like -Duration 604800 so it expires after a week?
Setting -Duration 18001
is the minimum number that works. 🤯
If the request doesn't contain a a pode.sid cookie or header, or the SessionId is invalid, then Pode will regenerate a new one each time. So is the default behavior that using session middleware generates a cookie unless you specify header?
I think I've got it!
If you're able to update the Pode module locally, what happens if you change the following line:
to be:
$expiry = $Session.Properties.TimeStamp.ToUniversalTime()
instead? :D
I span up a server in Central US when we were first looking at this and CSRF was fine. But your 18001s is bang on UTC -05:00, so figured I'd try again. BOOM, this time it stopped working for me too!
Turns out, the expiry is read from the cookie, but is converted to local time, not UTC. So when it check UTC Now vs Local Now it's expired. Changing that one line to convert to UTC fixed it on my VM 😄
And yes, session middleware will use cookies by default, unless -UseHeader
is specified when enabling sessions.
Badgerati for the win!
Ah UTC the DNS of time 🤣 . This should go down in Pode bug history.
This should go down in Pode bug history
Hahah, yes definitely! 😂
I've pushed a more full commit to fix this (and the missing Add-PodeEndpoint in the docs!). I'll look at getting this out into a v2.4.2 will some other bugs - rather than waiting for v2.5.0! 😃
Question
Attempting to use the example at the bottom of https://badgerati.github.io/Pode/Tutorials/Middleware/Types/CSRF/
The view generates the webpage, but when pressing submit Pode says the CSRF token is invalid.