@duhfrazee @danramteke
With one of the recent updates to Vapor, the cookie same-site policy defaults to lax
(previously it was none
). Unfortunately lax
is not sufficient as Apple is using POST requests for their redirection and lax
only works with GET
. You can find more information here: "Sign in with Apple" implementation hurdles - DEV Community 👩💻👨💻
Quick-fix - Insecure
A quick-fix would be to use your own SessionsConfiguration
when initializing the SessionsMiddleware
. Works for testing, but you shouldn’t do this on production (opens you up to CSRF attacks).
in configure.swift
:
let sessionsMiddleware = SessionsMiddleware(
session: app.sessions.driver,
configuration: .init(
cookieName: "SIWA",
cookieFactory: { sessionID -> HTTPCookies.Value in
return .init(
string: sessionID.string,
expires: Date(timeIntervalSinceNow: 60*10), // 10 minutes
maxAge: nil,
domain: nil,
path: "/web/auth/siwa/",
isSecure: false,
isHTTPOnly: false,
sameSite: HTTPCookies.SameSitePolicy.none
)
}
)
)
app.middleware.use(sessionsMiddleware)
There is another solution that is not using the existing SessionsMiddleware
(so you can still use it properly and securely for the rest of your site) posted by 0xTim in Vapor Discord:
Proper-fix - creating a separate Cookie just for SIWA
Create a separate middleware, which will check if there is a state
in our session and copy it into a separate Cookie with same-site policy none
:
import Vapor
struct SignInWithAppleStateMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
next.respond(to: request).map { response in
if let state = request.session.data["state"] {
let expiryDate = Date().addingTimeInterval(300)
let cookie = HTTPCookies.Value(string: state, expires: expiryDate, maxAge: 300, isSecure: false, isHTTPOnly: true, sameSite: HTTPCookies.SameSitePolicy.none)
response.cookies["state"] = cookie
}
return response
}
}
}
In configure.swift
, add SignInWithAppleStateMiddleware
after SessionsMiddleware
:
app.middleware.use(SignInWithAppleStateMiddleware())
In SIWAViewController.swift
, update callback(req)
and replace the guard
statement with:
guard
let sessionState = req.cookies["state"]?.string,
!sessionState.isEmpty,
sessionState == auth.state else {
return req.eventLoop.makeFailedFuture(UserError.siwaInvalidState)
}
We will update the tutorial soon!