-
Notifications
You must be signed in to change notification settings - Fork 55
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
Implement oidc #628
base: master
Are you sure you want to change the base?
Implement oidc #628
Conversation
# Conflicts: # build.gradle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I don't like is there's too much business logic in ServerLaunche
, I would ideally like to move it to a new file like OidcHandlers.java
or to the existing UserHandlers
class.
I have cleaned up the code and made sure everything works. Here are a few things worth mentioning:
Please let me know if there are any questions or concerns. I have tested this and from my view this could be merged edit: Found the alternative for |
src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java
Outdated
Show resolved
Hide resolved
src/main/java/me/kavin/piped/server/handlers/auth/UserHandlers.java
Outdated
Show resolved
Hide resolved
for (int i = 0; i < Constants.OIDC_PROVIDERS.size(); i++) { | ||
OidcProvider curr = Constants.OIDC_PROVIDERS.get(i); | ||
if (curr == null || !curr.name.equals(provider)) continue; | ||
return curr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (int i = 0; i < Constants.OIDC_PROVIDERS.size(); i++) { | |
OidcProvider curr = Constants.OIDC_PROVIDERS.get(i); | |
if (curr == null || !curr.name.equals(provider)) continue; | |
return curr; | |
} | |
for (OidcProvider curr : Constants.OIDC_PROVIDERS) { | |
if (curr != null && curr.name.equals(provider)) return curr; | |
} | |
.state(new State(state)) | ||
.nonce(data.getOidNonce()).build(); | ||
|
||
if (redirectUri.equals(Constants.FRONTEND_URL + "/login")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably shouldn't hardcode the url path here, maybe it makes sense to parse the domain of the redirectUri
and see if equals Constants.FRONTEND_URL
? Or we could just do a redirectUri.startsWith(Constants.FRONTEND_URL + "/")
.
return HttpResponse.ok200().withHtml( | ||
"<!DOCTYPE html><html style=\"color-scheme: dark light;\"><body>" + | ||
"<h3>Warning:</h3> You are trying to give <pre style=\"font-size: 1.2rem;\">" + | ||
redirectUri + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redirectUri
needs to be HTML escaped or checked if it's a real URL, otherwise that would allow HTML injection.
|
||
OidcData data = DatabaseHelper.getOidcData(authResponse.getState().toString()); | ||
if (data == null) { | ||
return HttpResponse.ofCode(400).withHtml( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't provide any styling or color-scheme: dark light;
, withPlainText
would probably be better to not become flashed in dark mode
AuthorizationCode code = authResponse.getAuthorizationCode(); | ||
|
||
if (code == null) { | ||
return HttpResponse.ofCode(400).withHtml( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above (withPlainText
)
return HttpResponse.ofCode(400).withHtml("Received a bad token. Please try again"); | ||
} catch (JOSEException e) { | ||
System.err.println("Token processing error: " + e); | ||
return HttpResponse.ofCode(500).withHtml("Internal processing error. Please try again"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
withPlainText
UserInfoResponse userInfoResponse = UserInfoResponse.parse(ur.toHTTPRequest().send()); | ||
|
||
if (!userInfoResponse.indicatesSuccess()) { | ||
return HttpResponse.ofCode(500).withHtml( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
withPlainText
|
||
cr.select(root).where(root.get("sub").in(sub)); | ||
|
||
OidcUserData dbuser = s.createQuery(cr).uniqueResult(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
OidcUserData dbuser = s.createQuery(cr).uniqueResult(); | |
OidcUserData dbUser = s.createQuery(cr).uniqueResult(); |
for (OidcProvider oidcProvider : Constants.OIDC_PROVIDERS) { | ||
if (oidcProvider.name.equals(oidcUserData.getProvider())) { | ||
provider = oidcProvider; | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (OidcProvider oidcProvider : Constants.OIDC_PROVIDERS) { | |
if (oidcProvider.name.equals(oidcUserData.getProvider())) { | |
provider = oidcProvider; | |
break; | |
} | |
} | |
provider = ServerLauncher.getOidcProvider(oicdUserDate.getProvider()); |
AuthenticationSuccessResponse sr = parseOidcUri(requestUri); | ||
|
||
OidcData data = DatabaseHelper.getOidcData(sr.getState().toString()); | ||
|
||
if (data == null) { | ||
return HttpResponse.ofCode(400).withHtml( | ||
"Your oidc provider sent invalid state data. Try again or contact your oidc admin" | ||
); | ||
} | ||
|
||
String redirect = data.data.split("\\|")[1]; | ||
String session = data.data.split("\\|")[0]; | ||
|
||
URI callback = new URI(Constants.PUBLIC_URL + "/oidc/" + provider.name + "/delete"); | ||
AuthorizationCode code = sr.getAuthorizationCode(); | ||
|
||
if (code == null) { | ||
return HttpResponse.ofCode(400).withHtml( | ||
"Your oidc provider sent an invalid code. Try again or contact your oidc admin" | ||
); | ||
} | ||
|
||
AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, callback, data.getOidVerifier()); | ||
|
||
ClientAuthentication clientAuth = new ClientSecretBasic(provider.clientID, provider.clientSecret); | ||
|
||
TokenRequest tokenRequest = new TokenRequest.Builder(provider.tokenUri, clientAuth, codeGrant).build(); | ||
TokenResponse tokenResponse = OIDCTokenResponseParser.parse(tokenRequest.toHTTPRequest().send()); | ||
|
||
if (!tokenResponse.indicatesSuccess()) { | ||
TokenErrorResponse errorResponse = tokenResponse.toErrorResponse(); | ||
return HttpResponse.ofCode(500).withHtml("Failure while trying to request token:\n\n" + errorResponse.getErrorObject().getDescription()); | ||
} | ||
|
||
OIDCTokenResponse successResponse = (OIDCTokenResponse) tokenResponse.toSuccessResponse(); | ||
|
||
JWT idToken = JWTParser.parse(successResponse.getOIDCTokens().getIDTokenString()); | ||
|
||
IDTokenClaimsSet claims; | ||
try { | ||
claims = provider.validator.validate(idToken, data.getOidNonce()); | ||
} catch (BadJOSEException e) { | ||
System.err.println("Invalid token received: " + e); | ||
return HttpResponse.ofCode(400).withHtml("Received a bad token. Please try again"); | ||
} catch (JOSEException e) { | ||
System.err.println("Token processing error: " + e); | ||
return HttpResponse.ofCode(500).withHtml("Internal processing error. Please try again"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's just a lot of duplicate code from oidcLoginCallback
, it would certainly make sense to refactor the common code into a new method.
see TeamPiped/Piped#2571