By default, keycloak.js
uses a Session status iframe to detect if the user has logged out from the application in another browser tab/window.
Unfortunately, this feature doesn't work in some modern browsers, unless SSL/TLS is enabled.
As a workaround, we can conditionally disable the feature (using the checkLoginIframe
option) if there is no SSL/TLS by checking if the webpage protocol is HTTPS, e.g.
keycloak.init({
// ...
initOptions: {
// ...
checkLoginIframe: window.location.protocol === 'https:',
// ...
},
// ...
});
TestRestTemplate
only ignores redirects and cookies if Apache Http Client is on the classpath.
Passing JWT tokens in a header (e.g. Authorization header) protects against CSRF attacks.
Avoid adding IDE-specific paths to project repository's .gitignore
files, as you then need to add them for every project and for every IDE used by contributors. Instead, add them to a global .gitignore
file, e.g.
git config --global core.excludesFile ~/.gitignore
The best way to define constant versions is to create a version catalog with a gradle/libs.versions.toml file.
The best way to share build logic between subprojects is to use convention plugins (https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html). Cross project configuration (subprojects
/allprojects
) is now discouraged (https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins_vs_cross_configuration).
Properties can be shared using gradle.properties. Properties can be overridden in subprojects by creating another gradle.properties
at the subproject level.
Advantages of Gradle over Maven:
- Version Conflict Resolution: Gradle selects the highest version while Maven uses a nearest-first strategy (https://docs.gradle.org/current/userguide/dependency_resolution.html#sub:resolution-strategy). Maven's strategy can be problematic because it may resolve to a lower version of a transitive dependency than what a dependency requires, and fails at runtime. This can be mitigated using maven-enforcer-plugin's requireUpperBoundDeps rule.
- Configuration Reuse: Maven uses parent POMs, which does not allow multiple inheritance. Gradle uses plugins, which allow multiple inheritance and are much more powerful. Plugins can even be defined in the same project using convention plugins.
- Maven configuration uses XML, while Gradle configuration uses Groovy/Kotlin code, which is much more powerful (e.g. create new tasks, add dependencies between tasks, etc).
- To publish a test library in Maven, the tests need to be packaged as a separate library. Gradle has test fixtures.
- Incremental build that is reliable. No more having to run
clean
before every build!
Enum names can be used as compile-time constants using Lombok's @FieldNameConstants, by setting @FieldNameConstants#onlyExplicitlyIncluded
to true
, and annotating enum fields with @FieldNameConstants.Include
(see projectlombok/lombok#2731 (comment)), e.g.
@FieldNameConstants(onlyExplicitlyIncluded = true)
public enum Level {
@FieldNameConstants.Include LOW,
@FieldNameConstants.Include MEDIUM,
@FieldNameConstants.Include HIGH,
;
}
To get the PID of a running Java process, run:
jps
(requires a JDK to be on the PATH
)
To capture a heap dump of a running Java process, run:
jmap -dump:live,format=b,file=/output/file/path.hprof <PID>
(requires a JDK to be on the PATH
)
To export a module as a CommonJS, UMD and ES module:
// ES: directly export the module features
export function isPrime(x) {
// ...
}
// CommonJS/UMD: export namespace
export as namespace mathLib;
Usage example:
<!-- UMD -->
<script src="math-lib.js"></script>
<script>
mathLib.isPrime( ... );
</script>
// CommonJS
var mathLib = require('math-lib');
mathLib.isPrime( ... );
// ES
import { isPrime } from "math-lib";
isPrime( ... );
For libraries, enable Explicit API Mode to improve the library's public API, e.g.
// build.gradle.kts
kotlin {
explicitApi()
}
To build with a lower version of Kotlin, use the latest Kotlin JVM Gradle plugin , but set kotlin.coreLibrariesVersion
to the target Kotlin version, e.g.
# gradle/libs.versions.toml
[versions]
kotlinLib = "1.6.10"
kotlinPlugin = "1.8.22"
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinPlugin" }
// build.gradle.kts
kotlin {
coreLibrariesVersion = libs.versions.kotlinLib.get()
}
The version of the Kotlin JVM Gradle plugin cannot be defined more than once in the classpath (even if it is the same version everywhere). As a workaround, define the plugin at the root project, and disable it if it is not used in the root project, e.g.
# gradle/libs.versions.toml
[versions]
kotlin = "1.8.22"
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
// build.gradle.kts
plugins {
alias(libs.plugins.kotlin.jvm) // Kotlin Gradle plugin must be loaded in the parent/root project
}
# gradle.properties
kotlinVersion=1.8.22
// build.gradle.kts
plugins {
id("org.jetbrains.kotlin.jvm") version "${kotlinVersion}" apply false // Kotlin Gradle plugin must be loaded in the parent/root project
}
If relative paths are used for client Valid Redirect URIs, they will be relative to the client root URL, which defaults to the auth server root URL. This is especially useful when used with a configuration as code tool like keycloak-config-cli, because the clients' Valid Redirect URIs does not need to be customized for each environment (e.g. sqa, prod).
Since Selenium 4.6
, there is no longer a need to manually install web drivers, Selenium will automatically download the right web drivers if they are not specified/found (https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location/#use-the-latest-version-of-selenium).
Use a user token instead of username/password for publishing artifacts to Maven Central. To generate a user token, login to the Sonatype Nexus Repository Manager (https://oss.sonatype.org/), then go to Profile -> User Token -> Access -> Access User Token
.
When configuring the path of a resource files from classpath (e.g. src/main/resources
) in a @ConfigurationProperties
class, instead of using String (and loading it using ClassLoader#getResourceAsStream()
), use a Resource
(and load it using Resource#getInputStream()
).
The configuration file can then use the classpath:
/classpath*:
prefix to specify that it is a ClassPathResource
(e.g. classpath:path/to/resource/file
). This gives the property flexibility to specify other kinds of Resources
, e.g. https:
, file:
, etc. (see https://docs.spring.io/spring-framework/reference/core/resources.html).
If a @Configuration
class does not use inter-bean references, the proxyBeanMethods
parameter for the @Configuration
annotation can be set to false
to disable CGLIB subclass processing. This is usually used in Spring Boot Auto-configuration libraries.
The docs for @LocalServerPort suggests using field injection. But to use field injection in Kotlin, the field needs to be lateinit
, or needs to have a default value, and lateinit
does not work with primitives, and @LocalServerPort
does not seem to work with delegated properties. Since @LocalServerPort
is an Int
, the field must be typed as Number
instead to work with lateinit
. A much easier way is to annotate the test class with @TestConstructor
and inject @LocalServerPort
in the constructor instead.
Since Spring Boot 2.3.x
, the ClassLoader
implementation used in Spring Boot executable JARs, LaunchedURLClassLoader, loads application classes (in BOOT-INF/classes
) before library classes (in BOOT-INF/lib
) (see spring-projects/spring-boot#9128 and spring-projects/spring-boot#45b1ab4). This means that we can override classes in libraries with classes in the application by placing it in the same classpath. This is especially useful for core/customization projects, because resource files can be overridden by giving it the same path, without having to change the path configuration.
To open web links with the default browser on Windows:
- Install wslu
- Set
export BROWSER=wslview
in WSL
Use wslgit instead of Git for Windows, because:
- Git for Windows doesn't preserve file mode bits (which is used for permissions)
wslgit
allows running Git hooks in WSL (where programming tools should be installed/ran)
Install git-credential-manager (GCM) for Windows standalone (https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/install.md#standalone-installation). Configure git in WSL to use GCM for Windows (https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/wsl.md#configuring-wsl-without-git-for-windows).
To use KDiff3 as the diff tool in both Windows and WSL, configure the following in .gitconfig
(WSL):
[diff]
guitool = kdiff3
[difftool]
prompt = false
[difftool "kdiff3"]
path = "/mnt/c/Program Files/KDiff3/bin/kdiff3.exe"
# Unix style paths must be converted to windows path style
cmd = kdiff3.exe \"`wslpath -w $LOCAL`\" \"`wslpath -w $REMOTE`\"
trustExitCode = false
[merge]
tool = kdiff3
[mergetool]
keepBackup = false
prompt = false
path = "/mnt/c/Program Files/KDiff3/bin/kdiff3.exe"
trustExitCode = false
To install Jetbrains IDEs (e.g. Intellij IDEA) in WSL:
- Install
libfuse2
,libgtk-3-bin
,mesa-utils
,libnss3
andlibasound2
:sudo apt update; sudo apt install libfuse2 libgtk-3-bin mesa-utils libnss3 libasound2
- Download Jetbrains Toolbox for Linux (
.tar.gz
) - Untar the
.tar.gz
file and run it, which will install Jetbrains Toolbox in~/.local/share/JetBrains/Toolbox
- Add
~/.local/share/JetBrains/Toolbox/bin
and~/.local/share/JetBrains/Toolbox/scripts
to$PATH
- Run Jetbrains Toolbox using
jetbrains-toolbox
. Use Jetbrains Toolbox to install IDEs
Webpack DevServer proxy (and by extension, Angular proxy) bypass
requires target
to be configured, even if it is not used (eg. proxy to file using fs.createReadStream())
REST API for Quarkus distribution (the default since Keycloak 17) no longer has /auth
in the context path. Set KC_HTTP_RELATIVE_PATH
to /auth
to re-introduce it (https://www.keycloak.org/migration/migrating-to-quarkus#_default_context_path_changed)
The official Docker image for Keycloak is located at Quay.io (quay.io/keycloak/keycloak). Since Keycloak 17 though, the official Docker images are also published to Docker Hub (keycloak/keycloak). See keycloak/keycloak#11987 for more information.
Official Keycloak Docker image does not define a default ENTRYPOINT
. A command needs to be passed to /opt/keycloak/bin/kc.sh
. Use:
start-dev
for development (https://www.keycloak.org/server/configuration#_development_versus_production_mode)build
to create a customized and optimized container image (https://www.keycloak.org/server/containers#_creating_a_customized_and_optimized_container_image)start
to run in production.
Use npm link to test libraries locally. Use --save
to save the file:
reference to package.json
and package-lock.json
to better indicate that you are linking to a local dependency (but don't commit the changes to Git).
If provider
has dependencies (deps
), function passed to useFunction
cannot be an arrow function.
- When pinning a Docker image to a specific SHA256 digest, the digest for the overall manifest list should be used, so that it will work on any OS/ARCH platform.
- Unfortunately, Docker Hub UI does not display the overall manifest list digest (see docker/roadmap#262). Until it is implemented, the current workaround to get the latest overall manifest list digest is to either pull the image tag without specifying a digest, or using the Docker Hub Registry API (docker/roadmap#262 (comment)).
The job DSL API for plugins can be viewed at <Jenkins URL>/plugin/job-dsl/api-viewer/index.html
.
To restart Jenkins from the web UI, go to <Jenkins URL>/safeRestart
.
- To override a scalar property (e.g.
String
) of a plugin's configuration that was defined in a parent POM asnull
, usecombine.self="override"
(normally, scalar properties will be directly overridden, while vector properties will be merged. However, empty/null
child scalar properties will be ignored). For example:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier combine.self="override"/>
</configuration>
</plugin>
</plugins>
</build>
Android Chrome browser doesn't have DevTools. To debug a web application in Android, connect it to a desktop (also works with emulators), then go to chrome://inspect/#devices
on the desktop's Chrome browser to remote target the Android Chrome browser.
If Chrome browser can't load or keeps crashing in Android emulator, disable Vulkan by adding the following to ~/.android/advancedFeatures.ini
:
Vulkan = off
GLDirectMem = on
To test Flutter Web applications on mobile browsers, run it on Web Server (flutter run -d web-server
), then in an Android emulator, open Chrome and navigate to http://10.0.2.2:<port>
(10.0.2.2
is the Host IP Address for AVD, and will differ for different emulators).
If a test needs to wait until all Promises complete before asserting (e.g. code that uses ExtendableEvent.waitUntil()), use:
await Promise(process.nextTick);
// assertions
expect(...)
Data.HashMap.Strict and Data.HashSet from unordered-containers are Haskell's implementation of hashtables (similar to Java's HashMap and HashSet).
For maintain insertion order, use Data.HashMap.Strict.InsOrd or Data.HashSet.InsOrd from insert-ordered-containers (note that they are from different libraries, and do not extend a common class), which are Haskell's implementation of hashtables with linked list (similar to Java's LinkedHashMap and LinkedHashSet).
To sort by natural order (e.g. to sort Strings alphabetically), use Data.Set from containers (note that they are from different libraries, and do not extend a common class), which are Haskell's implementation of self-balancing binary tree (similar to Java's TreeMap and TreeSet).
Signing commits ensures that the committer is who they claim to be. To autosign all commits with GPG:
- Install
gpg
if not already installed. E.g. on Windows, install gpg4win, which comes with Kleopatra, a GUI tool to manage certificates. - Generate a new RSA secret key using
gpg --full-generate-key
. Choose the ECC key algorithm and Curve 25519 elliptic curve. - Publish the public key to a public keyserver (e.g. GitHub account settings).
- List all public/private keys with their long form key IDs with
gpg --list-secret-keys --keyid-format=long
. - Copy the key ID of the private/secret key (
sec
) (after e.g.ed25519/
). - Run
git config --global user.signingKey <key ID>!
to configure the secret key to use. Note the exclaimation mark (!
) after the key ID, this is used to force GPG to use the specified key and not try to calculate which key to use (see https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html). - Run
git config --global commit.gpgsign true
to configure the Git client to automatically sign all commits with GPG. - On Windows, it may be required to run
git config --global gpg.program <path to gpg>
(Git for Windows defaults to using the embeddedgpg
). The path togpg
can be found by runningwhere gpg
(where.exe gpg
in PowerShell). - Add the public key to the Git server, e.g. for GitHub, see https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account.
- One of the biggest pitfalls in Golang is the lack of file-level scopes. Files are imported by package. There is no way to differentiate between package and file scope. That means that even private members (members with names that start with lowercase) can be accessed by another file within the same package.