mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-02 02:13:52 +00:00
fix(auth): restore oauth redirect and gitlab email checks
This commit is contained in:
parent
d1459aea83
commit
caa89d54ff
3 changed files with 119 additions and 12 deletions
|
|
@ -56,7 +56,7 @@ spring:
|
|||
scope:
|
||||
- read:user
|
||||
- user:email
|
||||
redirect-uri: ${SKILLHUB_PUBLIC_BASE_URL:http://localhost:8080}/login/oauth2/code/github
|
||||
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||
client-name: GitHub
|
||||
authorization-grant-type: authorization_code
|
||||
gitlab:
|
||||
|
|
@ -66,7 +66,7 @@ spring:
|
|||
- read_user
|
||||
- email
|
||||
authorization-grant-type: authorization_code
|
||||
redirect-uri: ${SKILLHUB_PUBLIC_BASE_URL:http://localhost:8080}/login/oauth2/code/gitlab
|
||||
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||
client-name: ${OAUTH2_GITLAB_DISPLAY_NAME:GitLab}
|
||||
provider:
|
||||
github:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.iflytek.skillhub.auth.oauth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
|
@ -26,8 +27,8 @@ public class GitLabClaimsExtractor implements OAuthClaimsExtractor {
|
|||
|
||||
private final RestClient restClient;
|
||||
|
||||
public GitLabClaimsExtractor() {
|
||||
this.restClient = RestClient.builder()
|
||||
public GitLabClaimsExtractor(RestClient.Builder restClientBuilder) {
|
||||
this.restClient = restClientBuilder
|
||||
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
||||
.build();
|
||||
}
|
||||
|
|
@ -45,9 +46,7 @@ public class GitLabClaimsExtractor implements OAuthClaimsExtractor {
|
|||
// GitLab returns email directly in user info
|
||||
String email = (String) attrs.get("email");
|
||||
|
||||
// GitLab provides email_verified field in the user info response
|
||||
Boolean emailVerifiedObj = (Boolean) attrs.get("email_verified");
|
||||
boolean emailVerified = emailVerifiedObj != null && emailVerifiedObj;
|
||||
boolean emailVerified = isConfirmed(attrs.get("confirmed_at"));
|
||||
|
||||
log.debug("Initial email from GitLab: {}, verified: {}", email, emailVerified);
|
||||
|
||||
|
|
@ -129,10 +128,15 @@ public class GitLabClaimsExtractor implements OAuthClaimsExtractor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents a GitLab email object from the /user/emails API.
|
||||
*
|
||||
* @param email the email address
|
||||
* @param confirmed whether the email has been confirmed
|
||||
* GitLab marks a confirmed email by populating confirmed_at.
|
||||
*/
|
||||
private record GitLabEmail(String email, boolean confirmed) {}
|
||||
private record GitLabEmail(String email, @JsonProperty("confirmed_at") String confirmedAt) {
|
||||
boolean confirmed() {
|
||||
return confirmedAt != null && !confirmedAt.isBlank();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isConfirmed(Object confirmedAt) {
|
||||
return confirmedAt instanceof String value && !value.isBlank();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package com.iflytek.skillhub.auth.oauth;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
class GitLabClaimsExtractorTest {
|
||||
|
||||
@Test
|
||||
void extract_marksProfileEmailVerifiedWhenConfirmedAtPresent() {
|
||||
RestClient.Builder restClientBuilder = RestClient.builder();
|
||||
GitLabClaimsExtractor extractor = new GitLabClaimsExtractor(restClientBuilder);
|
||||
|
||||
OAuthClaims claims = extractor.extract(
|
||||
userRequest(),
|
||||
new DefaultOAuth2User(
|
||||
java.util.List.of(),
|
||||
Map.of(
|
||||
"id", 42,
|
||||
"username", "alice",
|
||||
"email", "alice@gitlab.example",
|
||||
"confirmed_at", "2026-04-16T08:00:00Z"
|
||||
),
|
||||
"username"
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(claims.email()).isEqualTo("alice@gitlab.example");
|
||||
assertThat(claims.emailVerified()).isTrue();
|
||||
assertThat(claims.providerLogin()).isEqualTo("alice");
|
||||
}
|
||||
|
||||
@Test
|
||||
void extract_loadsConfirmedEmailFromEmailListWhenProfileEmailIsUnconfirmed() {
|
||||
RestClient.Builder restClientBuilder = RestClient.builder();
|
||||
MockRestServiceServer server = MockRestServiceServer.bindTo(restClientBuilder).build();
|
||||
server.expect(requestTo("https://gitlab.example.com/api/v4/user/emails"))
|
||||
.andExpect(header(HttpHeaders.AUTHORIZATION, "Bearer token-123"))
|
||||
.andRespond(withSuccess(
|
||||
"""
|
||||
[
|
||||
{"email":"alice@gitlab.example","confirmed_at":"2026-04-16T08:00:00Z"},
|
||||
{"email":"alice+pending@gitlab.example","confirmed_at":null}
|
||||
]
|
||||
""",
|
||||
MediaType.APPLICATION_JSON
|
||||
));
|
||||
GitLabClaimsExtractor extractor = new GitLabClaimsExtractor(restClientBuilder);
|
||||
|
||||
OAuthClaims claims = extractor.extract(
|
||||
userRequest(),
|
||||
new DefaultOAuth2User(
|
||||
java.util.List.of(),
|
||||
Map.of(
|
||||
"id", 42,
|
||||
"username", "alice",
|
||||
"email", "alice+pending@gitlab.example"
|
||||
),
|
||||
"username"
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(claims.email()).isEqualTo("alice@gitlab.example");
|
||||
assertThat(claims.emailVerified()).isTrue();
|
||||
server.verify();
|
||||
}
|
||||
|
||||
private OAuth2UserRequest userRequest() {
|
||||
ClientRegistration registration = ClientRegistration.withRegistrationId("gitlab")
|
||||
.clientId("client-id")
|
||||
.clientSecret("client-secret")
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
|
||||
.scope("read_user", "email")
|
||||
.authorizationUri("https://gitlab.example.com/oauth/authorize")
|
||||
.tokenUri("https://gitlab.example.com/oauth/token")
|
||||
.userInfoUri("https://gitlab.example.com/api/v4/user")
|
||||
.userNameAttributeName("username")
|
||||
.clientName("GitLab")
|
||||
.build();
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(
|
||||
OAuth2AccessToken.TokenType.BEARER,
|
||||
"token-123",
|
||||
Instant.now(),
|
||||
Instant.now().plusSeconds(3600)
|
||||
);
|
||||
return new OAuth2UserRequest(registration, accessToken);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue