Human-Readable Description of Credentials with Service Names
The generateServiceName() function available in the IntelliJ SDK API helps name credentials in a consistent way so that they can be easily recognized in password managers and when users are asked to allow the IDE to access a secret. Pass a subsystem name that identifies the plugin or its area of functionality, and a key that identifies the specific secret, such as an account name for a password or server name for an access token.
import com.intellij.credentialStore.generateServiceName
val serviceName =
generateServiceName("My Service", "john.doe")
@Service
class PasswordService {
suspend fun save(username: String, password: String) {
withContext(Dispatchers.IO) {
PasswordSafe.instance
.setPassword(credentialAttributesOf(username), password)
}
}
private fun credentialAttributesOf(username: String): CredentialAttributes {
// see above for definition
}
}
@Service
public final class PasswordService {
@RequiresBackgroundThread
public void save(String username, String password) {
PasswordSafe.getInstance()
.setPassword(credentialAttributesOf(username), password);
}
private CredentialAttributes credentialAttributesOf(String username) {
// see above for definition
}
}
Use the getPassword() method with these credential attributes to query for the password.
suspend fun find(username: String): String? = withContext(Dispatchers.IO) {
PasswordSafe.instance.getPassword(credentialAttributesOf(username))
}
@RequiresBackgroundThread
public String find(String username) {
return PasswordSafe.getInstance()
.getPassword(credentialAttributesOf(username));
}
API Key Management
API key management is broadly similar to password management, with two distinct changes.
The service name can be a constant string.
The userName property in CredentialAttributes is not set.
@Service
class ApiKeyService {
private val serviceName =
generateServiceName("My Credentials Storage", "API Key")
suspend fun save(apiKey: String) = withContext(Dispatchers.IO) {
PasswordSafe.instance
.setPassword(CredentialAttributes(serviceName), apiKey)
}
suspend fun load(): String? = withContext(Dispatchers.IO) {
PasswordSafe.instance
.getPassword(CredentialAttributes(serviceName))
}
}
@Service
public final class ApiKeyService {
private static final String SERVICE_NAME = CredentialAttributesKt
.generateServiceName("My Credentials Storage", "API Key");
@RequiresBackgroundThread
public void save(String apiKey) {
PasswordSafe.getInstance()
.setPassword(new CredentialAttributes(SERVICE_NAME), apiKey);
}
@RequiresBackgroundThread
public String load() {
return PasswordSafe.getInstance()
.getPassword(new CredentialAttributes(SERVICE_NAME));
}
}
Private Keys and Other Non-String Credentials
When storing complex credentials that are not easily representable by strings, such as private keys, use map-like behavior of PasswordSafe. Use CredentialAttributes as keys, while Credential instances represent values.
Use the following PasswordSafe methods:
operator fun set(attributes: CredentialAttributes, credentials: Credentials?)
operator fun get(attributes: CredentialAttributes): Credentials?
public void set(CredentialAttributes attributes, @Nullable Credentials credentials);
public @Nullable Credentials get(CredentialAttributes attributes);
Construct a Credential instance that allows setting both the username and credential value from String instances, byte arrays, and char arrays.
When retrieving Credential values, their credentials are represented by OneTimeString, a wrapper of sensitive data that can be used and cleared. Such string can be serialized to standard String instances, arrays of characters, or arrays of bytes.
@Service
class PrivateKeyService {
suspend fun save(identity: String, privateKey: PrivateKey) = withContext(Dispatchers.IO) {
val credentialAttributes = credentialAttributesOf(identity)
privateKey.encoded?.let { keyBytes: ByteArray ->
PasswordSafe.instance[credentialAttributes] = Credentials(identity, keyBytes)
}
}
suspend fun find(identity: String): PrivateKey? = withContext(Dispatchers.IO) {
val credentialAttributes = credentialAttributesOf(identity)
val credentials: Credentials? = PasswordSafe.instance[credentialAttributes]
val keyBytes: ByteArray? = credentials?.password?.toByteArray()
keyBytes?.let {
loadPrivateKey(keyBytes)
}
}
private fun credentialAttributesOf(identity: String): CredentialAttributes
// see password management example
private fun loadPrivateKey(keyBytes: ByteArray): PrivateKey {
// implement as necessary
}
}
@Service
public final class PrivateKeyService {
@RequiresBackgroundThread
public void save(String identity, PrivateKey key) {
byte[] keyBytes = key.getEncoded();
if (keyBytes == null) {
return;
}
CredentialAttributes attributes = createCredentialAttributes(identity);
PasswordSafe.getInstance().set(attributes, new Credentials(identity, keyBytes));
}
@RequiresBackgroundThread
public PrivateKey find(String identity) {
CredentialAttributes credentialAttributes = createCredentialAttributes(identity);
Credentials credentials = PasswordSafe.getInstance().get(credentialAttributes);
if (credentials == null) {
return null;
}
OneTimeString password = credentials.getPassword();
if (password == null) {
return null;
}
byte[] keyBytes = password.toByteArray();
if (keyBytes == null) {
return null;
}
return loadPrivateKey(keyBytes);
}
private CredentialAttributes credentialAttributesOf(String identity) {
// see password management example
}
private PrivateKey loadPrivateKey(byte[] keyBytes) {
// implement as necessary
}
}
Removing Credentials
To remove stored credentials, pass null for the credentials parameter.
Retrieving Credentials in Remote Development Context
For Remote Development, PasswordSafe provides an alternative way to retrieve credentials in Kotlin code.
suspend fun getAsync(attributes: CredentialAttributes): Ephemeral<Credentials>
Besides being coroutine-friendly, it returns "ephemeral" credentials that are valid only while the client is connected to the backend in the Remote Development context. When the client disconnects, the credentials are erased, preventing further actions on the user's behalf.
Users can override the default behavior Settings | Appearance & Behavior | System Settings | Passwords.
Storage in Remote Development Context
Since 2025.3, credentials are transparently redirected to the frontend and stored according to the local environment and settings, such as KeePass or macOS Keychain.
Before 2025.3, credentials are stored on the backend side in plain text.