DEV Community

Masui Masanori
Masui Masanori

Posted on

[Micronaut] Try Cookie

Setting cookies on redirect

First, I will set a cookie value on redirect.
I created two micronaut applications.

When a user access the application1, it redirects to application2 and set a cookie.

[Application1] HomeController.java

package jp.masanori;

import java.util.Optional;
import java.net.URI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.cookie.Cookies;
import io.micronaut.http.cookie.SameSite;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.annotation.CookieValue;

@Controller("/")
public class HomeController {
    private final Logger logger;
    public HomeController() {
        this.logger = LoggerFactory.getLogger(HomeController.class);
    }
    @Get("/")
    public String index(@CookieValue("SESSION-VALUE") Optional<String> sessionValue,
            HttpRequest<String> req) {
        if (sessionValue.isPresent()) {
            logger.debug("OK: " + sessionValue.get());
        } else {
            logger.debug("No cookie");
        }
        Cookies c = req.getCookies();
        for (var v : c.getAll()) {
            logger.debug("--------Cookie---------");
            logger.debug("Name:" + v.getName());
            logger.debug("Value: " + v.getValue());
            logger.debug("Domain:" + v.getDomain());
            logger.debug("Path: " + v.getPath());
            logger.debug("MaxAge: " + v.getMaxAge());
            logger.debug("SameSite: " + v.getSameSite());
            logger.debug("HttpOnly: " + v.isHttpOnly());
            logger.debug("Secure: " + v.isSecure());
        }
        return "Hello World!";
    }
    @Get("/redirect")
    public MutableHttpResponse<Object> redirectToOutTasks() {
        URI location = URI.create("http://ourtasks.masanori.jp:8083/ourtasks/pages/cookie");

        Cookie ck = Cookie.of("SESSION-VALUE", "Hello Micronaut!");
        return HttpResponse.redirect(location)
            .cookie(ck);
    }
}
Enter fullscreen mode Exit fullscreen mode

[Application2] PageController.java

package jp.masanori.apps;

import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.CookieValue;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.views.View;

@Controller("/pages")
public class PageController {
    private final Logger logger;

    public PageController() {
        this.logger = LoggerFactory.getLogger(PageController.class);
    }

    @Produces(MediaType.TEXT_HTML)
    @Get("/cookie")
    @View("editTask")
    public HttpResponse<String> getCookieSample(@CookieValue("SESSION-VALUE") Optional<String> sessionValue) {
        if (sessionValue.isPresent()) {
            logger.debug("OK: " + sessionValue.get());
        } else {
            logger.debug("No cookie");
        }
        return HttpResponse.ok();
    }
}
Enter fullscreen mode Exit fullscreen mode

Set cookies into subdomains

By defaut, the application2 can't get the cookie value.
When I access sample.masanori.jp:8086(HomeController.index() of application1 is called),
I will get logs like below.

--------Cookie---------
Name:SESSION-VALUE
Value: Hello Micronaut!
Domain:null
Path: null
MaxAge: -9223372036854775808
SameSite: Optional.empty
HttpOnly: false
Secure: false
Enter fullscreen mode Exit fullscreen mode

Because their domains are just added into the hosts file of Windows,
so if I change the application1 domain into "ourtasks.masanori.jp:8086", the application2 can get the cookie value.

hosts

...
127.0.0.1 ourtasks.masanori.jp
127.0.0.1 sample.masanori.jp
Enter fullscreen mode Exit fullscreen mode

To share cookie values between their domains, I will add some options.

[Application1] HomeController.java

...
    @Get("/redirect")
    public MutableHttpResponse<Object> redirectToOutTasks() {
        URI location = URI.create("http://ourtasks.masanori.jp:8083/ourtasks/pages/cookie");

        Cookie ck = Cookie.of("SESSION-VALUE", "Hello Micronaut!!");
        // To avoid accessing the cookie value from the client-side
        ck.httpOnly(true);
        // Share the cookie value on "masanori.jp" or "*.masanori.jp".
        // Both the application1 and application2 have to belong to these domains. 
        ck.domain("masanori.jp");
        ck.sameSite(SameSite.Strict);
        return HttpResponse.redirect(location)
            .cookie(ck);
    }
}
Enter fullscreen mode Exit fullscreen mode

Set cookies for Undertow

When I use Under tow as a web server, the above code could not set cookies.
(Although I can see the value on a web browser, but I could not get it on my application)
So I should add "Set-Cookie" into the HttpResponse headers.

build.gradle

...
micronaut {
    runtime("undertow")
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("jp.masanori.*")
    }
    aot {
...
    }
}
...
Enter fullscreen mode Exit fullscreen mode

PageController.java

...
    @Get("/jwt")
    public MutableHttpResponse<?> tryJwt(@CookieValue("JWT-VALUE") Optional<String> sessionValue) {
        if (sessionValue.isPresent()) {
            logger.info("OK: " + sessionValue.get());
            return HttpResponse.ok();
        } else {
            return HttpResponse.ok()
                    // I could not use "cookie()" for Undertow
                    /* .cookie(ck) */
                    .header("Set-Cookie", String.format("JWT-VALUE=%s;path=/;HttpOnly;", "Hello"));
        }
    }
...
Enter fullscreen mode Exit fullscreen mode

Set JWT to cookies

I will try setting JWT as a cookie value using java-jwt.

PageController.java


import java.util.Date;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;

import io.micronaut.core.util.CollectionUtils;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.CookieValue;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.views.View;
import io.micronaut.http.cookie.Cookies;

@Controller("/pages")
public class PageController {
...
    @Get("/jwt")
    public MutableHttpResponse<?> tryJwt(@CookieValue("JWT-VALUE") Optional<String> sessionValue) {
        if (sessionValue.isPresent()) {
            if (validateJwt(sessionValue)) {
                return HttpResponse.ok();
            } else {
                return HttpResponse.unauthorized();
            }
        } else {
            return HttpResponse.ok()
                    /* .cookie(ck) */
                    .header("Set-Cookie", String.format("JWT-VALUE=%s;path=/;HttpOnly;", generateJwt()));
        }
    }

    private String generateJwt() {
        try {
            Date expireTime = new Date();
            expireTime.setTime(expireTime.getTime() + 10000);

            Algorithm algorithm = Algorithm.HMAC256("secret");
            return JWT.create()
                    .withClaim("message", "hello")
                    .withIssuer("masanori")
                    .withExpiresAt(expireTime)
                    .sign(algorithm);
        } catch (JWTCreationException exception) {
            logger.error("JWT Error: ", exception);
        }
        return null;
    }

    private boolean validateJwt(Optional<String> value) {
        if (value.isPresent() == false) {
            return false;
        }
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("masanori")
                    .build();
            DecodedJWT jwt = verifier.verify(value.get());
            Claim message = jwt.getClaim("message");
            if (message.isMissing() == false && message.isNull() == false) {
                logger.info("decoded message: " + message.asString());
            }
            return true;
        } catch (JWTVerificationException exception) {
            logger.error("JWT validate failed ", exception);
            return false;
        }
    }
...
Enter fullscreen mode Exit fullscreen mode

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs