Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded hosted auth to PKCE #59

Merged
merged 3 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions passage/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ android {

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
buildConfig = true
}
Expand Down Expand Up @@ -93,9 +93,9 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp:4.10.0"

// For JSON serialization and parsing
implementation "com.squareup.moshi:moshi:1.14.0"
implementation "com.squareup.moshi:moshi-adapters:1.14.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0"
implementation "com.squareup.moshi:moshi:1.15.1"
implementation "com.squareup.moshi:moshi-adapters:1.15.1"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.1"


// For safely encrypting stored data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ internal class HostedTests {
runTest {
try {
val invalidAuthCode = "INVALID_AUTH_CODE"
passage.hostedAuthFinish(invalidAuthCode, "", "")
passage.hostedAuthFinish(invalidAuthCode, "")
fail("Test should throw FinishOIDCAuthenticationInvalidRequestException")
} catch (e: Exception) {
assertThat(e is HostedAuthorizationError)
Expand Down
27 changes: 23 additions & 4 deletions passage/src/main/java/id/passage/android/Passage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,28 @@ import id.passage.android.api.OAuth2API
import id.passage.android.api.OTPAPI
import id.passage.android.api.RegisterAPI
import id.passage.android.api.UsersAPI
import id.passage.android.exceptions.*
import id.passage.android.exceptions.AppInfoException
import id.passage.android.exceptions.FinishSocialAuthenticationException
import id.passage.android.exceptions.GetMagicLinkStatusException
import id.passage.android.exceptions.GetMagicLinkStatusNotFoundException
import id.passage.android.exceptions.HostedAuthorizationError
import id.passage.android.exceptions.HostedLogoutException
import id.passage.android.exceptions.LoginException
import id.passage.android.exceptions.LoginNoExistingUserException
import id.passage.android.exceptions.LoginNoFallbackException
import id.passage.android.exceptions.LoginWithPasskeyException
import id.passage.android.exceptions.MagicLinkActivateException
import id.passage.android.exceptions.NewLoginMagicLinkException
import id.passage.android.exceptions.NewLoginOneTimePasscodeException
import id.passage.android.exceptions.NewRegisterMagicLinkException
import id.passage.android.exceptions.NewRegisterOneTimePasscodeException
import id.passage.android.exceptions.OneTimePasscodeActivateException
import id.passage.android.exceptions.PassageTokenException
import id.passage.android.exceptions.RegisterException
import id.passage.android.exceptions.RegisterNoFallbackException
import id.passage.android.exceptions.RegisterPublicDisabledException
import id.passage.android.exceptions.RegisterUserExistsException
import id.passage.android.exceptions.RegisterWithPasskeyException
import id.passage.android.model.ActivateMagicLinkRequest
import id.passage.android.model.ActivateOneTimePasscodeRequest
import id.passage.android.model.AuthenticatorAttachment
Expand Down Expand Up @@ -688,18 +709,16 @@ public final class Passage(
* This method completes the hosted authentication process by exchanging the provided authorization code for Passage tokens.
*
* @param code The code returned from app link redirect to your activity.
* @param clientSecret You hosted app's client secret, found in Passage Console's OIDC Settings.
* @param state The state returned from app link redirect to your activity.
* @throws HostedAuthorizationError
*/

public suspend fun hostedAuthFinish(
code: String,
clientSecret: String,
state: String,
): Pair<PassageAuthResult, String> {
try {
val finishHostedAuthResult = PassageHosted.finishHostedAuth(code, clientSecret, state)
val finishHostedAuthResult = PassageHosted.finishHostedAuth(code, state)
finishHostedAuthResult.let { (authResult, idToken) ->
handleAuthResult(authResult)
handleIdToken(idToken)
Expand Down
11 changes: 4 additions & 7 deletions passage/src/main/java/id/passage/android/PassageHosted.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ internal class PassageHosted {
packageName = activity.packageName
val redirectUri = "$basePathOIDC/android/$packageName/callback"
state = Utils.getRandomString()
val randomString = Utils.getRandomString()
verifier = Utils.getRandomString()
val codeChallenge = Utils.sha256Hash(randomString)
val codeChallenge = Utils.sha256Hash(verifier)
val newParams =
listOf(
"client_id" to appId,
Expand All @@ -53,7 +52,6 @@ internal class PassageHosted {

internal suspend fun finishHostedAuth(
code: String,
clientSecret: String,
state: String,
): Pair<AuthResult, String> {
val redirectUri = "$basePathOIDC/android/$packageName/callback"
Expand All @@ -76,8 +74,7 @@ internal class PassageHosted {
"grant_type" to "authorization_code",
"code" to code,
"client_id" to Passage.appId,
"verifier" to verifier,
"client_secret" to clientSecret,
"code_verifier" to verifier,
"redirect_uri" to redirectUri,
).joinToString("&") { (key, value) ->
"$key=${URLEncoder.encode(value, "UTF-8")}"
Expand Down Expand Up @@ -123,15 +120,15 @@ internal class PassageHosted {
idToken: String,
) {
val redirectUri = "$basePathOIDC/android/$packageName/logout"
verifier = Utils.getRandomString()
state = Utils.getRandomString()
val url =
Uri
.parse("$basePathOIDC/logout")
.buildUpon()
.appendQueryParameter("id_token_hint", idToken)
.appendQueryParameter("client_id", appId)
.appendQueryParameter("post_logout_redirect_uri", redirectUri)
.appendQueryParameter("state", verifier)
.appendQueryParameter("state", state)
.build()

val customTabsIntent = CustomTabsIntent.Builder().build()
Expand Down