<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Charles Etieve</title>
    <description>The latest articles on DEV Community by Charles Etieve (@charlesetieve).</description>
    <link>https://dev.to/charlesetieve</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1059179%2F474d7852-6861-4689-8973-80dbce24c742.jpeg</url>
      <title>DEV Community: Charles Etieve</title>
      <link>https://dev.to/charlesetieve</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/charlesetieve"/>
    <language>en</language>
    <item>
      <title>Store JWT Token with Coroutines</title>
      <dc:creator>Charles Etieve</dc:creator>
      <pubDate>Wed, 05 Apr 2023 06:28:18 +0000</pubDate>
      <link>https://dev.to/charlesetieve/store-jwt-token-with-coroutines-4bn5</link>
      <guid>https://dev.to/charlesetieve/store-jwt-token-with-coroutines-4bn5</guid>
      <description>&lt;p&gt;JWT (JSON Web Token) has become a popular standard for implementing stateless authentication in modern mobile apps. In order to maintain security and avoid session hijacking, it's important to store JWT tokens securely on the client-side.&lt;/p&gt;

&lt;p&gt;DataStore is a popular choice for storing tokens as it provides the advantages of shared preferences along with additional coroutines capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store token (must be called from suspend function)
dataStore.edit { it[KEY_TOKEN] = token }
// read token (returns a flow of token)
dataStore.data.map { it[KEY_TOKEN] }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One powerful use case is to navigate to the home or login view on wether the token exists or not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dataStore.data
        .map { it.contains(KEY_TOKEN) }
        .onEach { isAuthenticated -&amp;gt;
                when(isAuthenticated) {
                        true -&amp;gt; navController.navigate("home")
                        false -&amp;gt; navController.navigate("login")
                }
        }.launchIn(scope)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However &lt;strong&gt;DataStore lacks support for encryption&lt;/strong&gt;, whereas shared preferences does. This means developers have to choose between the convenience of coroutines and the security of encryption when choosing a method to store their JWT tokens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mggwaymq3nqkfzwgm7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mggwaymq3nqkfzwgm7c.png" alt="Meme about adroid developer choosing between coroutines and encryption for JWT Token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, there is a library &lt;strong&gt;&lt;a href="https://github.com/osipxd/encrypted-datastore" rel="noopener noreferrer"&gt;Encrypted DataStore&lt;/a&gt;&lt;/strong&gt; that &lt;strong&gt;extends DataStore and allows for easy encryption&lt;/strong&gt;. While this solution is currently useful, it may become deprecated in the future once DataStore natively implements this functionality.&lt;/p&gt;

&lt;p&gt;Enough talk, let's dive into the code.&lt;/p&gt;

&lt;p&gt;First, import libraries in your build.gradle (app module):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// datastore
implementation("androidx.datastore:datastore-preferences:1.0.0")
// extension for datastore to support encryption
implementation("io.github.osipxd:security-crypto-datastore-preferences:1.0.0-alpha04")
// utility library for datastore encryption
implementation("androidx.security:security-crypto-ktx:1.1.0-alpha05")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you will need to build your datastore to inject it in your AuthenticationService:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val dataStore = PreferenceDataStoreFactory.createEncrypted{
    EncryptedFile.Builder(
        context,
      // the file should have extension .preferences_pb
        dataStoreFile("filename.preferences_pb"),
      MasterKey
          .Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build(),
      EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally you can add this service to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map

class AuthenticationService(
    private val dataStore: DataStore&amp;lt;Preferences&amp;gt;
) {

        // returns a flow of is authenticated state
    fun isAuthenticated(): Flow&amp;lt;Boolean&amp;gt; {
                // flow of token existence from dataStore
        return dataStore.data.map {
                it.contains(KEY_TOKEN)
                }
        }

        // store new token after sign in or token refresh
    suspend fun store(token: String) {
                // store token to dataStore
        dataStore.edit {
            it[KEY_TOKEN] = token
                }
        }

        // get token for protected API method
    suspend fun getToken(): String {
            return dataStore.data
                  .map { it[KEY_TOKEN] } // get a flow of token from dataStore
          .firstOrNull() // transform flow to suspend
              ?: throw IllegalArgumentException("no token stored")
    }

        // to call when user logs out or when refreshing the token has failed
    suspend fun onLogout() {
                // remove token from dataStore
        dataStore.edit {
            it.remove(KEY_TOKEN)
                }
        }

    companion object {
        val KEY_TOKEN = stringPreferencesKey("key_token")
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In conclusion, there is now an efficient and secure data storage option available for your JWT tokens, so there's no excuse to settle for anything less! 😊&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>security</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
