I've learned in this SO question that there currently is no simple way to turn cookie-based CSRF tokens into HTTP request headers in Elm. Thus, to write a single page application (SPA) that works nicely with a Django Rest Framework backend, I need to manually retrieve the CSRF-Token from the corresponding cookie value.
How do I retrieve a cookie value in Elm? Does Elm provide runtime support for this via some Command? Or do I need to retrieve the cookie using plain JavaScript and provide it to the ELM SPA via a port?
As of Elm 0.9, you need to use Ports to read the cookie from JavaScript and pass it back to the Elm application.
In my application, I do the following. I define a fetchCsrfToken port that I use from Elm to call a JavaScript function that reads the cookie. That function then triggers a callback to Elm via a csrfTokenReciever port. My Elm application subscribes to that event via subscriptions.
-- Ports.elm
port fetchCsrfToken : () -> Cmd msg
port csrfTokenReciever : (String -> msg) -> Sub msg
-- Main.elm
init : Flags -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
-- ...
(model, Ports.fetchCsrfToken ())
subscriptions : Model -> Sub Msg
subscriptions model =
Ports.csrfTokenReciever GotCsrfToken
// index.js
app.ports.fetchCsrfToken.subscribe(function (str) {
const value = getCookie('csrftoken')
if (value === null) {
app.ports.csrfTokenReciever.send('')
} else {
app.ports.csrfTokenReciever.send(value)
}
})
Using Elm 0.19.1
First solution:
Use of 2 ports, a subscription and some JS/TS code like #viam0Zah mentioned.
Second solution:
Pass the CSRF into your flags at init
const app = Elm.Main.init({
node: document.querySelector("main"),
flags: {
csrfToken: getCookie('csrftoken')
}
});
add csrfToken to Flags
type alias Flags =
{ ---
, csrfToken : String
}
And don't forget to add a decoder for the csrfToken:
import Json.Decode as D
flagsDecoder : D.Decoder Flags
flagsDecoder =
D.succeed Flags
|> ---
|> D.required "csrfToken" D.string
If you want to be more robust and extend type safety for both solutions - flags and ports, you should check out https://elm-ts-interop.com/, it's just amazing!
Related
I have a webapp made with Next.js and Apollo as show in example with-apollo. I want to serve multiple domains with my webapp (name-based virtual hosting). Unfortunately HttpLink of ApolloClient requires absolute server URL with domain but this makes backend app unable to recognize domain which user really visited. Is there a way to configure HttpLink with a dynamic URL based on real request or use relative URL or anything else?
Either use an Apollo Link to intercept the query and set uri property on the context
const authMiddleware = setContext((operation, { uri }) => {
return refreshToken().then(res => ({
uri: this.getURI()
})
}))
Or intercept the request with Angular's HttpClient interceptor and change the endpoint.
https://github.com/apollographql/apollo-angular/tree/master/packages/apollo-angular-link-http#options
Source: Updating uri of apollo client instance
The NextPageContext object passed to getInitialProps includes the req object when called on the server-side. So you can do something like:
WithApollo.getInitialProps = async ctx => {
const { AppTree, req } = ctx
const linkBaseUrl = req ? req.protocol + '://' + req.get('host') : ''
...
}
You can then pass this base url down to createApolloClient along with the initial state and prepend your HttpLink's url with it. On the client side, this will prepend an empty string (you only need the full URL on the server).
EDIT: See my post below for answer how to fix this.
I am building a client app that will store some data in the user dropbox app folder. So currently I am using implicit grant that will redirect the user to the given redirect uri with the parameters passed after the # in the url
Example:
localhost:1666/Dropbox#access_token=...&token_type=.......
Creating a http listener over the localhost url it detects the request however everything after # is ignored and is not passed as part of the request. Is there a way to make capture the data after the #, or is there any other library that allows me to do so?
I am using the cpprestsdk https://github.com/microsoft/cpprestsdk
web::http::experimental::listener::http_listener* l = new web::http::experimental::listener::http_listener(m_authConfig.redirect_uri());
l->support([this](web::http::http_request request) -> void
{
auto uri = request.request_uri();
auto requestPath = web::uri::split_path(uri.path());
auto queryObjects = web::uri::split_query(uri.query());
auto s = uri.fragment();
if (request.request_uri().path() == U("/Dropbox")) && request.request_uri().query() != U(""))
{
request.reply(web::http::status_codes::OK, U("ok.") + uri.query());
}
else
{
request.reply(web::http::status_codes::OK, U("error.") + uri.query());
}
});
l->open().wait();
Thanks!
So after researching a bit, it turns out that # (fragments) are not sent back in most browsers, so to be able to get the data i return the following java-script script:
<script> window.location.replace([location.protocol, '//', location.host, location.pathname, '?', location.hash.substring(1,location.hash.length )].join(''));</script>
This will convert the hash part to a query string and redirect it the user to it so that the listener detects it.
I upgraded from ASP.NET Core 1.1 to 2.0 and am now having 401 Unauthorized responses get changed to 302 Redirect responses. This was previously an issue for me in 1.1 and was mitigated with the following code:
services.AddIdentity<User, IdentityRole>(identityOptions =>
{
identityOptions.Cookies.ApplicationCookie.AutomaticChallenge = false;
})
However, there is no longer a Cookies property on identityOptions.
I have tried adding the following as well (and also note that I previously did not need this extension method in my app):
services.AddCookieAuthentication(cookieAuthenticationOptions => {
cookieAuthenticationOptions.LoginPath = ""; // also tried null
cookieAuthenticationOptions.AccessDeniedPath = ""; // also tried null
cookieAuthenticationOptions.LogoutPath = ""; // also tried null
});
That code appears to have no effect to the default redirect paths or behaviors. How can I prevent these redirects in Core 2.0?
As explained in https://github.com/aspnet/Announcements/issues/262, you must now configure the default scheme handlers at the global level, using the services.AddAuthentication() extension.
To prevent the cookies handlers registered by Identity from handling challenges, replace DefaultChallengeScheme by the scheme corresponding to a different handler (e.g the JWT bearer handler).
services.AddIdentity<User, IdentityRole>();
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
});
If - for whatever reason - choosing a different handler is not an option for you, then you'll have to use services.ConfigureApplicationCookie() to register a custom CookieAuthenticationEvents.(On)RedirectToLogin event to change the way Identity returns a "unauthorized response".
Here's an example returning a 401 response:
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
I am using HtmlUnit Driver for generating a headless browser. I need the cookie information run the tests ahead. While i am able to inspect the elements i am unable to derive the cookie informations.
Please help.
You can get Cookies information from HtmlUnitDriver using driver.manage().getCookies(); (driver is instance of HtmlUnitDriver).
Here is the sample Java code that prints Cookie name and its value:
Set<Cookie> allCookies = driver.manage().getCookies();
for (Cookie cookie : allCookies) {
System.out.println(String.format( "%s -> %s" , cookie.getName(), cookie.getValue()));
}
Additionally if you are aware of the name of Cookie for which you want to get value , you can directly use method , getCookieName.
Method Name: getCookieNamed(java.lang.String name)
Syntax: driver.manage().getCookieNamed(CookieName);
Purpose: To Get a cookie with a given name.
Arguments: CookieName- the name of the cookie
Returns: It will return the cookie value for the name specified, or null if no cookie found with the given name
Here is what i used.
Cookie cookie = new Cookie("key", "value");
driver.manage().addCookie(cookie);
Set<Cookie> allCookies = driver.manage().getCookies();
for (Cookie loadedCookie : allCookies) {
System.out.println(String.format("%s -> %s", loadedCookie.getName(), loadedCookie.getValue()));
}
I am trying to Post and get a cookie. I am a newbie and this is a learning project for me. My impression is that if you use 'set-cookie' one should be able to see an additional 'set-cookie' in the .toSource. (I am trying to accomplish this on Google Apps Site if that makes a difference.) Am I missing something? Here is my code:
function setGetCookies() {
var payload = {'set-cookie' : 'test'};
var opt2 = {'headers':payload, "method":"post"};
UrlFetchApp.fetch("https://sites.google.com/a/example.com/blacksmith", opt2);
var response = UrlFetchApp.fetch("https://sites.google.com/a/example.com/blacksmith")
var openId = response.getAllHeaders().toSource();
Logger.log(openId)
var AllHeaders = response.getAllHeaders();
for (var prop in AllHeaders) {
if (prop.toLowerCase() == "set-cookie") {
// if there's only one cookie, convert it into an array:
var myArray = [];
if ( Array.isArray(AllHeaders[prop]) ) {
myArray=AllHeaders[prop];
} else {
myArray[0]=AllHeaders[prop];
}
// now process the cookies
myArray.forEach(function(cookie) {
Logger.log(cookie);
});
break;
}
}
}
Thanks in advance! I referenced this to develop the code: Cookie handling in Google Apps Script - How to send cookies in header?
Open to any advice.
When you aren't logged in Google Sites won't set any cookies in the response. UrlFetchApp doesn't pass along your Google cookies, so it will behave as if you are logged out.
First the cookie you want to send whose name is 'test' does not have a value. You should send 'test=somevalue'.
Second I am wondering if you are trying to send the cookie to the googlesite server and ask it to reply with the same cookie you previously sent... ?
I am thinking you are trying to act as a HTTP server beside you are a HTTP client.
As a HTTP client your role is only to send back any cookies that the HTTP server have previously sent to you (respecting the domain, expiration... params).