-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package id.passage.android | ||
|
||
import android.app.Activity | ||
import android.net.Uri | ||
import androidx.browser.customtabs.CustomTabsIntent | ||
import com.squareup.moshi.JsonClass | ||
import com.squareup.moshi.Moshi | ||
import id.passage.android.model.AuthResult | ||
import id.passage.client.infrastructure.ClientException | ||
import id.passage.client.infrastructure.ServerException | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
import okhttp3.MediaType.Companion.toMediaType | ||
import okhttp3.OkHttpClient | ||
import okhttp3.Request | ||
import okhttp3.RequestBody.Companion.toRequestBody | ||
import java.net.URLEncoder | ||
import java.security.MessageDigest | ||
import java.security.SecureRandom | ||
import java.util.Base64 | ||
|
||
internal class PassageOIDC { | ||
internal companion object { | ||
internal var verifier = "" | ||
private const val CODE_CHALLENGE_METHOD = "S256" | ||
private const val SECRET_STRING_LENGTH = 32 | ||
|
||
internal fun openChromeTab( | ||
appId: String, | ||
Check failure on line 29 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L29 <standard:parameter-list-spacing>
Raw output
Check failure on line 29 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L29 <standard:no-multi-spaces>
Raw output
|
||
activity: Activity, | ||
authUrl: String, | ||
) { | ||
val redirectUri = "${Passage.BASE_PATH_OIDC}/android/${Passage.Package_NAME}/callback" | ||
val state = getRandomString() | ||
val randomString = getRandomString() | ||
verifier = randomString | ||
val codeChallenge = sha256Hash(randomString) | ||
val newParams = listOf( | ||
Check failure on line 38 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L38 <standard:multiline-expression-wrapping>
Raw output
|
||
"client_id" to appId, | ||
"redirect_uri" to redirectUri, | ||
"state" to state, | ||
"code_challenge" to codeChallenge, | ||
"code_challenge_method" to CODE_CHALLENGE_METHOD, | ||
"scope" to "openid", | ||
"response_type" to "code", | ||
).joinToString("&") { | ||
(key, value) -> | ||
Check failure on line 47 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L47 <standard:function-literal>
Raw output
|
||
"$key=${URLEncoder.encode(value, "UTF-8")}" | ||
} | ||
val url = "${authUrl}?${newParams}" | ||
Check failure on line 50 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L50 <standard:string-template>
Raw output
Check failure on line 50 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L50 <standard:string-template>
Raw output
|
||
val intent = CustomTabsIntent.Builder().build() | ||
intent.launchUrl(activity, Uri.parse(url)) | ||
} | ||
|
||
private fun getRandomString(): String { | ||
val digits = '0'..'9' | ||
val upperCaseLetters = 'A'..'Z' | ||
val lowerCaseLetters = 'a'..'z' | ||
val characters = | ||
(digits + upperCaseLetters + lowerCaseLetters) | ||
.joinToString("") | ||
val random = SecureRandom() | ||
val stringBuilder = StringBuilder(SECRET_STRING_LENGTH) | ||
for (i in 0 until SECRET_STRING_LENGTH) { | ||
val randomIndex = random.nextInt(characters.length) | ||
stringBuilder.append(characters[randomIndex]) | ||
} | ||
return stringBuilder.toString() | ||
} | ||
|
||
private fun sha256Hash(randomString: String): String { | ||
val bytes = randomString.toByteArray() | ||
val md = MessageDigest.getInstance("SHA-256") | ||
val digest = md.digest(bytes) | ||
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest) | ||
} | ||
|
||
|
||
Check failure on line 78 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L78 <standard:no-consecutive-blank-lines>
Raw output
|
||
internal suspend fun finishOIDC(code: String) : AuthResult? { | ||
Check failure on line 79 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L79 <standard:function-return-type-spacing>
Raw output
Check failure on line 79 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L79 <standard:colon-spacing>
Raw output
|
||
val redirectUri = "${Passage.BASE_PATH_OIDC}/android/${Passage.Package_NAME}/callback" | ||
var authResult : AuthResult? | ||
Check failure on line 81 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L81 <standard:colon-spacing>
Raw output
|
||
val client = OkHttpClient() | ||
val moshi = Moshi.Builder() | ||
Check failure on line 83 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L83 <standard:multiline-expression-wrapping>
Raw output
Check failure on line 83 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L83 <standard:chain-method-continuation>
Raw output
|
||
.build() | ||
val jsonAdapter = moshi.adapter(OIDCResponse::class.java) | ||
val mediaType = "application/json; charset=utf-8".toMediaType() | ||
val requestBody = "{\"code\":\"$code\"}".toRequestBody(mediaType) | ||
|
||
val params = listOf( | ||
Check failure on line 89 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L89 <standard:multiline-expression-wrapping>
Raw output
|
||
"grant_type" to "authorization_code", | ||
"code" to code, | ||
"client_id" to Passage.appId, | ||
"verifier" to verifier, | ||
"client_secret" to Passage.clientID, | ||
"redirect_uri" to redirectUri | ||
Check failure on line 95 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L95 <standard:trailing-comma-on-call-site>
Raw output
|
||
).joinToString("&") { (key, value) -> | ||
"$key=${URLEncoder.encode(value, "UTF-8")}" | ||
} | ||
|
||
val url = "${Passage.BASE_PATH_OIDC}/token?$params" | ||
val request = Request.Builder() | ||
Check failure on line 101 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L101 <standard:multiline-expression-wrapping>
Raw output
Check failure on line 101 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L101 <standard:chain-method-continuation>
Raw output
|
||
.url(url) | ||
.post(requestBody) | ||
.build() | ||
|
||
withContext(Dispatchers.IO) { | ||
client.newCall(request).execute().use { response -> | ||
if (!response.isSuccessful) { | ||
if (response.code == 500) | ||
throw ServerException("Server error : ${response.code} ${response.message}", response.code) | ||
Check failure on line 110 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L110 <standard:multiline-if-else>
Raw output
|
||
throw ClientException("Client error : ${response.code} ${response.message}", response.code) | ||
} | ||
val responseBody = response.body?.string() | ||
if (responseBody != null) { | ||
val apiResponse = jsonAdapter.fromJson(responseBody)!! | ||
authResult = AuthResult( | ||
Check failure on line 116 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L116 <standard:multiline-expression-wrapping>
Raw output
|
||
authToken = apiResponse.access_token, | ||
redirectUrl = "", | ||
refreshToken = apiResponse.refresh_token, | ||
refreshTokenExpiration = null | ||
Check failure on line 120 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L120 <standard:trailing-comma-on-call-site>
Raw output
|
||
) | ||
|
||
Check failure on line 122 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L122 <standard:no-blank-line-before-rbrace>
Raw output
|
||
} | ||
else | ||
Check failure on line 124 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L124 <standard:keyword-spacing>
Raw output
|
||
throw Exception("Response body is null : ${response.code} ${response.message}") | ||
Check failure on line 125 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L125 <standard:if-else-bracing>
Raw output
Check failure on line 125 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L125 <standard:multiline-if-else>
Raw output
|
||
} | ||
} | ||
return authResult | ||
} | ||
} | ||
} | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class OIDCResponse(val access_token: String, val refresh_token: String?) | ||
Check failure on line 134 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L134 <standard:class-signature>
Raw output
Check failure on line 134 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L134 <standard:class-signature>
Raw output
Check failure on line 134 in passage/src/main/java/id/passage/android/PassageOIDC.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/PassageOIDC.kt#L134 <standard:class-signature>
Raw output
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
@file:Suppress("RedundantVisibilityModifier") | ||
|
||
package id.passage.android.exceptions | ||
|
||
import id.passage.client.infrastructure.ClientException | ||
import id.passage.client.infrastructure.ServerException | ||
|
||
/** | ||
* Thrown when there's an error finishing OIDC login/sign up | ||
* | ||
* @see FinishOIDCException | ||
*/ | ||
public open class FinishOIDCException(message: String) : PassageException(message) { | ||
Check failure on line 13 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L13 <standard:class-signature>
Raw output
Check failure on line 13 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L13 <standard:class-signature>
Raw output
|
||
internal companion object { | ||
internal fun convert(e: Exception): FinishOIDCException { | ||
val message = e.message ?: e.toString() | ||
return when (e) { | ||
is ClientException -> convertClientException(e) | ||
is ServerException -> FinishOIDCServerException(message) | ||
else -> FinishOIDCException(message) | ||
} | ||
} | ||
|
||
private fun convertClientException(e: ClientException): FinishOIDCException { | ||
Check failure on line 24 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L24 <standard:function-expression-body>
Raw output
|
||
return when (e.statusCode.toString()) { | ||
"400" -> { | ||
FinishOIDCBadRequestException(e.message.toString()) | ||
} | ||
else -> { | ||
FinishOIDCException(e.message.toString()) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Thrown when server returns bad request due to the invalid info. | ||
*/ | ||
public class FinishOIDCBadRequestException(message: String) : FinishOIDCException(message) | ||
Check failure on line 40 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L40 <standard:class-signature>
Raw output
Check failure on line 40 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L40 <standard:class-signature>
Raw output
|
||
|
||
/** | ||
* Thrown when Passage internal server error occurs. | ||
*/ | ||
public class FinishOIDCServerException(message: String) : FinishOIDCException(message) | ||
Check failure on line 45 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L45 <standard:class-signature>
Raw output
Check failure on line 45 in passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt GitHub Actions / ktlint[ktlint] passage/src/main/java/id/passage/android/exceptions/FinishOIDCException.kt#L45 <standard:class-signature>
Raw output
|