Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
reg-app
Regapp
Commits
f2e51110
Commit
f2e51110
authored
Mar 16, 2021
by
michael.simon
Browse files
OIDC: add PKCE support for public SPA
https://tools.ietf.org/html/rfc7636
parent
d7e37193
Changes
5
Hide whitespace changes
Inline
Side-by-side
bwreg-entities/src/main/java/edu/kit/scc/webreg/entity/oidc/OidcFlowStateEntity.java
View file @
f2e51110
...
...
@@ -42,6 +42,12 @@ public class OidcFlowStateEntity extends AbstractBaseEntity {
@Column
(
name
=
"code"
,
length
=
256
)
private
String
code
;
@Column
(
name
=
"code_challange"
,
length
=
512
)
private
String
codeChallange
;
@Column
(
name
=
"code_challange_method"
,
length
=
64
)
private
String
codecodeChallangeMethod
;
@Column
(
name
=
"response_type"
,
length
=
256
)
private
String
responseType
;
...
...
@@ -171,4 +177,20 @@ public class OidcFlowStateEntity extends AbstractBaseEntity {
public
void
setRefreshToken
(
String
refreshToken
)
{
this
.
refreshToken
=
refreshToken
;
}
public
String
getCodeChallange
()
{
return
codeChallange
;
}
public
void
setCodeChallange
(
String
codeChallange
)
{
this
.
codeChallange
=
codeChallange
;
}
public
String
getCodecodeChallangeMethod
()
{
return
codecodeChallangeMethod
;
}
public
void
setCodecodeChallangeMethod
(
String
codecodeChallangeMethod
)
{
this
.
codecodeChallangeMethod
=
codecodeChallangeMethod
;
}
}
bwreg-service/src/main/java/edu/kit/scc/webreg/service/oidc/OidcOpLogin.java
View file @
f2e51110
...
...
@@ -11,7 +11,9 @@ import net.minidev.json.JSONObject;
public
interface
OidcOpLogin
{
String
registerAuthRequest
(
String
realm
,
String
responseType
,
String
redirectUri
,
String
scope
,
String
state
,
String
nonce
,
String
clientId
,
HttpServletRequest
request
,
HttpServletResponse
response
)
String
nonce
,
String
clientId
,
String
codeChallange
,
String
codeChallangeMethod
,
HttpServletRequest
request
,
HttpServletResponse
response
)
throws
IOException
,
OidcAuthenticationException
;
JSONObject
serveUserInfo
(
String
realm
,
String
tokeType
,
String
tokenId
,
HttpServletRequest
request
,
...
...
@@ -23,6 +25,6 @@ public interface OidcOpLogin {
JSONObject
serveUserJwt
(
String
realm
,
HttpServletRequest
request
,
HttpServletResponse
response
)
throws
OidcAuthenticationException
;
JSONObject
serveToken
(
String
realm
,
String
grantType
,
String
code
,
String
redirectUri
,
HttpServletRequest
request
,
HttpServletResponse
response
,
String
clientId
,
String
clientSecret
)
throws
OidcAuthenticationException
;
HttpServletResponse
response
,
String
clientId
,
String
clientSecret
,
String
codeVerifier
)
throws
OidcAuthenticationException
;
}
bwreg-service/src/main/java/edu/kit/scc/webreg/service/oidc/OidcOpLoginImpl.java
View file @
f2e51110
package
edu.kit.scc.webreg.service.oidc
;
import
java.io.IOException
;
import
java.nio.charset.StandardCharsets
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.PrivateKey
;
import
java.security.cert.X509Certificate
;
import
java.util.ArrayList
;
import
java.util.Base64
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.UUID
;
...
...
@@ -28,6 +32,7 @@ import com.nimbusds.jose.jwk.JWK;
import
com.nimbusds.jwt.JWTClaimsSet
;
import
com.nimbusds.jwt.SignedJWT
;
import
com.nimbusds.oauth2.sdk.Scope
;
import
com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod
;
import
com.nimbusds.oauth2.sdk.token.BearerAccessToken
;
import
com.nimbusds.oauth2.sdk.token.RefreshToken
;
import
com.nimbusds.openid.connect.sdk.OIDCTokenResponse
;
...
...
@@ -104,8 +109,11 @@ public class OidcOpLoginImpl implements OidcOpLogin {
public
String
registerAuthRequest
(
String
realm
,
String
responseType
,
String
redirectUri
,
String
scope
,
String
state
,
String
nonce
,
String
clientId
,
String
codeChallange
,
String
codeChallangeMethod
,
HttpServletRequest
request
,
HttpServletResponse
response
)
throws
IOException
,
OidcAuthenticationException
{
checkCodeChallange
(
codeChallange
,
codeChallangeMethod
);
OidcOpConfigurationEntity
opConfig
=
opDao
.
findByRealmAndHost
(
realm
,
request
.
getServerName
());
if
(
opConfig
==
null
)
{
...
...
@@ -123,6 +131,12 @@ public class OidcOpLoginImpl implements OidcOpLogin {
throw
new
OidcAuthenticationException
(
"unknown client"
);
}
if
(
clientConfig
.
getGenericStore
().
containsKey
(
"redirect_uri_regex"
))
{
if
(!
redirectUri
.
matches
(
clientConfig
.
getGenericStore
().
get
(
"redirect_uri_regex"
)))
{
throw
new
OidcAuthenticationException
(
"invalid redirect uri"
);
}
}
OidcFlowStateEntity
flowState
=
flowStateDao
.
createNew
();
flowState
.
setOpConfiguration
(
opConfig
);
flowState
.
setNonce
(
nonce
);
...
...
@@ -132,6 +146,8 @@ public class OidcOpLoginImpl implements OidcOpLogin {
flowState
.
setCode
(
UUID
.
randomUUID
().
toString
());
flowState
.
setRedirectUri
(
redirectUri
);
flowState
.
setValidUntil
(
new
Date
(
System
.
currentTimeMillis
()
+
(
30L
*
60L
*
1000L
)));
flowState
.
setCodeChallange
(
codeChallange
);
flowState
.
setCodecodeChallangeMethod
(
codeChallangeMethod
);
flowState
=
flowStateDao
.
persist
(
flowState
);
session
.
setOidcFlowStateId
(
flowState
.
getId
());
...
...
@@ -264,7 +280,8 @@ public class OidcOpLoginImpl implements OidcOpLogin {
@Override
public
JSONObject
serveToken
(
String
realm
,
String
grantType
,
String
code
,
String
redirectUri
,
HttpServletRequest
request
,
HttpServletResponse
response
,
String
clientId
,
String
clientSecret
)
throws
OidcAuthenticationException
{
HttpServletRequest
request
,
HttpServletResponse
response
,
String
clientId
,
String
clientSecret
,
String
codeVerifier
)
throws
OidcAuthenticationException
{
OidcFlowStateEntity
flowState
=
flowStateDao
.
findByCode
(
code
);
...
...
@@ -284,9 +301,31 @@ public class OidcOpLoginImpl implements OidcOpLogin {
throw
new
OidcAuthenticationException
(
"unknown client"
);
}
if
((!
clientConfig
.
getName
().
equals
(
clientId
))
||
(!
clientConfig
.
getSecret
().
equals
(
clientSecret
)))
{
if
(!
clientConfig
.
getName
().
equals
(
clientId
))
{
throw
new
OidcAuthenticationException
(
"unauthorized"
);
}
if
(
clientSecret
!=
null
&&
(!
clientConfig
.
getSecret
().
equals
(
clientSecret
)))
{
// client_id and client_secret is set, but secret is wrong
throw
new
OidcAuthenticationException
(
"unauthorized"
);
}
else
if
(
codeVerifier
!=
null
)
{
//check code verifier
// code_verifier must be SHA256(flowState.getCodeChallange)
try
{
MessageDigest
digest
=
MessageDigest
.
getInstance
(
"SHA-256"
);
byte
[]
encodedhash
=
digest
.
digest
(
codeVerifier
.
getBytes
(
StandardCharsets
.
UTF_8
));
String
checkStr
=
new
String
(
Base64
.
getUrlEncoder
().
withoutPadding
().
encode
(
encodedhash
));
if
(!
checkStr
.
equals
(
flowState
.
getCodeChallange
()))
{
logger
.
debug
(
"Code challange failed: {} <-> {}"
,
checkStr
,
flowState
.
getCodeChallange
());
throw
new
OidcAuthenticationException
(
"code verification failed"
);
}
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
OidcAuthenticationException
(
"cannot create hash at the moment. This is bad."
);
}
}
IdentityEntity
identity
=
flowState
.
getIdentity
();
...
...
@@ -556,5 +595,19 @@ public class OidcOpLoginImpl implements OidcOpLogin {
}
return
returnList
;
}
}
private
void
checkCodeChallange
(
String
codeChallange
,
String
codeChallangeMethod
)
throws
OidcAuthenticationException
{
if
(
codeChallange
!=
null
)
{
if
(
codeChallange
.
length
()
>
511
)
{
throw
new
OidcAuthenticationException
(
"Code challange is not acceptable"
);
}
}
if
(
codeChallangeMethod
!=
null
)
{
if
(!
CodeChallengeMethod
.
S256
.
toString
().
equals
(
codeChallangeMethod
))
{
throw
new
OidcAuthenticationException
(
"Code challange method is not supported"
);
}
}
}
}
bwreg-webapp/src/main/java/edu/kit/scc/webreg/oauth/OidcAuthorizationController.java
View file @
f2e51110
...
...
@@ -25,10 +25,12 @@ public class OidcAuthorizationController {
public
void
auth
(
@PathParam
(
"realm"
)
String
realm
,
@QueryParam
(
"response_type"
)
String
responseType
,
@QueryParam
(
"redirect_uri"
)
String
redirectUri
,
@QueryParam
(
"scope"
)
String
scope
,
@QueryParam
(
"state"
)
String
state
,
@QueryParam
(
"nonce"
)
String
nonce
,
@QueryParam
(
"client_id"
)
String
clientId
,
@QueryParam
(
"code_challenge"
)
String
codeChallange
,
@QueryParam
(
"code_challenge_method"
)
String
codeChallangeMethod
,
@Context
HttpServletRequest
request
,
@Context
HttpServletResponse
response
)
throws
IOException
,
OidcAuthenticationException
{
String
red
=
opLogin
.
registerAuthRequest
(
realm
,
responseType
,
redirectUri
,
scope
,
state
,
nonce
,
clientId
,
request
,
response
);
String
red
=
opLogin
.
registerAuthRequest
(
realm
,
responseType
,
redirectUri
,
scope
,
state
,
nonce
,
clientId
,
codeChallange
,
codeChallangeMethod
,
request
,
response
);
response
.
sendRedirect
(
red
);
}
...
...
bwreg-webapp/src/main/java/edu/kit/scc/webreg/oauth/OidcTokenController.java
View file @
f2e51110
...
...
@@ -33,13 +33,14 @@ public class OidcTokenController {
public
JSONObject
auth
(
@PathParam
(
"realm"
)
String
realm
,
@FormParam
(
"grant_type"
)
String
grantType
,
@FormParam
(
"code"
)
String
code
,
@FormParam
(
"redirect_uri"
)
String
redirectUri
,
@FormParam
(
"client_id"
)
String
clientId
,
@FormParam
(
"client_secret"
)
String
clientSecret
,
@FormParam
(
"code_verifier"
)
String
codeVerifier
,
@Context
HttpServletRequest
request
,
@Context
HttpServletResponse
response
)
throws
Exception
{
logger
.
debug
(
"Post token called for {} with code {} and grant_type {}"
,
realm
,
code
,
grantType
);
if
(
clientId
!=
null
&&
clientSecret
!=
null
)
{
return
opLogin
.
serveToken
(
realm
,
grantType
,
code
,
redirectUri
,
request
,
response
,
clientId
,
clientSecret
);
if
(
clientId
!=
null
&&
(
clientSecret
!=
null
||
codeVerifier
!=
null
)
)
{
return
opLogin
.
serveToken
(
realm
,
grantType
,
code
,
redirectUri
,
request
,
response
,
clientId
,
clientSecret
,
codeVerifier
);
}
String
auth
=
request
.
getHeader
(
"Authorization"
);
...
...
@@ -51,7 +52,7 @@ public class OidcTokenController {
new
String
(
Base64
.
decodeBase64
(
auth
.
substring
(
index
).
getBytes
())),
":"
,
2
);
if
(
credentials
.
length
==
2
)
{
return
opLogin
.
serveToken
(
realm
,
grantType
,
code
,
redirectUri
,
request
,
response
,
credentials
[
0
],
credentials
[
1
]);
return
opLogin
.
serveToken
(
realm
,
grantType
,
code
,
redirectUri
,
request
,
response
,
credentials
[
0
],
credentials
[
1
]
,
codeVerifier
);
}
}
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment