Skip to content

Commit 7db8fca

Browse files
authored
Merge pull request #21 from onelogin/copilot/fix-response-state-parameter
Add optional state parameter to OIDCConfiguration
2 parents 1982843 + cea0467 commit 7db8fca

File tree

5 files changed

+71
-0
lines changed

5 files changed

+71
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ OIDCConfiguration.Builder()
5454
.scopes(listOf("openid"))
5555
.isDebug(true)
5656
.loginHint("test@email.com")
57+
.state("custom-state-value")
5758
.build()
5859
```
5960

@@ -66,6 +67,7 @@ The supported parameters of the configuration are:
6667
| redirectUrl | Redirect Url specified in the OneLogin Application | Required |
6768
| scopes | List of scopes of the authorization token, it should include `openid` | Required |
6869
| loginHint | A string hint to the Authorization Server about the login identifier the End-User might use to log in | Optional |
70+
| state | An opaque value used to maintain state between the request and callback to prevent CSRF attacks. If not provided, the library will auto-generate one | Optional |
6971
| isDebug | Specifies if the instance of the library should be initialized in debug mode, which will log additional information | Optional |
7072

7173

oneloginoidc/src/main/java/com/onelogin/oidc/OIDCConfiguration.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class OIDCConfiguration private constructor(
88
internal val redirectUrl: String,
99
internal val scopes: List<String>,
1010
internal val loginHint: String?,
11+
internal val state: String?,
1112
internal val encryptionManager: EncryptionManager?,
1213
internal val debug: Boolean
1314
) {
@@ -18,6 +19,7 @@ class OIDCConfiguration private constructor(
1819
private var redirectUrl: String? = null
1920
private var scopes: List<String> = emptyList()
2021
private var loginHint: String? = null
22+
private var state: String? = null
2123
private var encryptionManager: EncryptionManager? = null
2224
private var debug: Boolean = false
2325

@@ -46,6 +48,11 @@ class OIDCConfiguration private constructor(
4648
return this
4749
}
4850

51+
fun state(state: String): Builder {
52+
this.state = state
53+
return this
54+
}
55+
4956
fun isDebug(debug: Boolean): Builder {
5057
this.debug = debug
5158
return this
@@ -79,6 +86,7 @@ class OIDCConfiguration private constructor(
7986
redirectUrl!!,
8087
scopes,
8188
loginHint,
89+
state,
8290
encryptionManager,
8391
debug
8492
)

oneloginoidc/src/main/java/com/onelogin/oidc/login/SignInManagerImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ internal class SignInManagerImpl(
100100

101101
if (!configuration.loginHint.isNullOrBlank()) authReqBuilder.setLoginHint(configuration.loginHint)
102102

103+
if (!configuration.state.isNullOrBlank()) authReqBuilder.setState(configuration.state)
104+
103105
return authReqBuilder.build()
104106
}
105107
}

oneloginoidc/src/test/java/com/onelogin/oidc/OIDCConfigurationTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,22 @@ class OIDCConfigurationTest {
7373
assertEquals(listOf("openid"), configuration.scopes)
7474
assertEquals("testHint", configuration.loginHint)
7575
}
76+
77+
@Test
78+
@TestRail
79+
fun testConfigurationWithStateParameter() {
80+
val configuration = OIDCConfiguration.Builder()
81+
.issuer("http://issuer.com")
82+
.clientId("clientId")
83+
.redirectUrl("redirectUrl")
84+
.scopes(listOf("openid"))
85+
.state("custom-state-123")
86+
.build()
87+
88+
assertEquals("http://issuer.com", configuration.issuer)
89+
assertEquals("clientId", configuration.clientId)
90+
assertEquals("redirectUrl", configuration.redirectUrl)
91+
assertEquals(listOf("openid"), configuration.scopes)
92+
assertEquals("custom-state-123", configuration.state)
93+
}
7694
}

oneloginoidc/src/test/java/com/onelogin/oidc/login/SignInManagerTest.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import kotlinx.coroutines.channels.Channel
1515
import kotlinx.coroutines.channels.produce
1616
import kotlinx.coroutines.runBlocking
1717
import net.openid.appauth.*
18+
import org.junit.Assert.assertEquals
1819
import org.junit.Before
1920
import org.junit.Test
2021
import org.junit.runner.RunWith
@@ -186,4 +187,44 @@ class SignInManagerTest {
186187
verify { authorizationService.performTokenRequest(any(), any()) }
187188
verify { callback.onError(any()) }
188189
}
190+
191+
@Test
192+
@TestRail
193+
fun signInUsesCustomStateWhenProvided() = runBlocking {
194+
val customState = "custom-state-value"
195+
val configWithState = OIDCConfiguration.Builder()
196+
.issuer("testIssuer")
197+
.scopes(listOf("openid"))
198+
.redirectUrl("redirectTest")
199+
.clientId("testClientId")
200+
.state(customState)
201+
.build()
202+
203+
val signInManagerWithState = SignInManagerImpl(
204+
configWithState,
205+
authorizationService,
206+
repository
207+
) { authRequest ->
208+
// Verify the authorization request contains the custom state
209+
assertEquals("Expected state to match custom state", customState, authRequest.state)
210+
signInFragment
211+
}
212+
213+
val serviceCallbackSlot = slot<AuthorizationService.TokenResponseCallback>()
214+
every { signInFragment.resultChannel }.returns(produce<Pair<AuthorizationResponse?, AuthorizationException?>> {
215+
send(spyResponse to null)
216+
} as Channel)
217+
every {
218+
authorizationService.performTokenRequest(
219+
any(),
220+
capture(serviceCallbackSlot)
221+
)
222+
} answers {
223+
serviceCallbackSlot.captured.onTokenRequestCompleted(mockk(), null)
224+
}
225+
226+
signInManagerWithState.signIn(activity, callback)
227+
228+
verify { callback.onSuccess(any()) }
229+
}
189230
}

0 commit comments

Comments
 (0)