Files
OpenIsle/backend/src/main/java/com/openisle/service/GithubAuthService.java
2025-09-18 14:42:25 +08:00

160 lines
5.2 KiB
Java

package com.openisle.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.openisle.model.Role;
import com.openisle.model.User;
import com.openisle.repository.UserRepository;
import com.openisle.service.AvatarGenerator;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
@Service
@RequiredArgsConstructor
public class GithubAuthService {
private final UserRepository userRepository;
private final RestTemplate restTemplate = new RestTemplate();
private final AvatarGenerator avatarGenerator;
@Value("${github.client-id:}")
private String clientId;
@Value("${github.client-secret:}")
private String clientSecret;
public Optional<AuthResult> authenticate(
String code,
com.openisle.model.RegisterMode mode,
String redirectUri,
boolean viaInvite
) {
try {
String tokenUrl = "https://github.com/login/oauth/access_token";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, String> body = new HashMap<>();
body.put("client_id", clientId);
body.put("client_secret", clientSecret);
body.put("code", code);
if (redirectUri != null) {
body.put("redirect_uri", redirectUri);
}
HttpEntity<Map<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<JsonNode> tokenRes = restTemplate.postForEntity(
tokenUrl,
request,
JsonNode.class
);
if (
!tokenRes.getStatusCode().is2xxSuccessful() ||
tokenRes.getBody() == null ||
!tokenRes.getBody().has("access_token")
) {
return Optional.empty();
}
String accessToken = tokenRes.getBody().get("access_token").asText();
HttpHeaders authHeaders = new HttpHeaders();
authHeaders.setBearerAuth(accessToken);
authHeaders.set(HttpHeaders.USER_AGENT, "OpenIsle");
HttpEntity<Void> entity = new HttpEntity<>(authHeaders);
ResponseEntity<JsonNode> userRes = restTemplate.exchange(
"https://api.github.com/user",
HttpMethod.GET,
entity,
JsonNode.class
);
if (!userRes.getStatusCode().is2xxSuccessful() || userRes.getBody() == null) {
return Optional.empty();
}
JsonNode userNode = userRes.getBody();
String username = userNode.hasNonNull("login") ? userNode.get("login").asText() : null;
String avatarUrl = userNode.hasNonNull("avatar_url")
? userNode.get("avatar_url").asText()
: null;
String email = null;
if (userNode.hasNonNull("email")) {
email = userNode.get("email").asText();
}
if (email == null || email.isEmpty()) {
try {
ResponseEntity<JsonNode> emailsRes = restTemplate.exchange(
"https://api.github.com/user/emails",
HttpMethod.GET,
entity,
JsonNode.class
);
if (
emailsRes.getStatusCode().is2xxSuccessful() &&
emailsRes.getBody() != null &&
emailsRes.getBody().isArray()
) {
for (JsonNode n : emailsRes.getBody()) {
if (n.has("primary") && n.get("primary").asBoolean()) {
email = n.get("email").asText();
break;
}
}
}
} catch (HttpClientErrorException ignored) {
// ignore when the email API is not accessible
}
}
if (email == null) {
email = username + "@users.noreply.github.com";
}
return Optional.of(processUser(email, username, avatarUrl, mode, viaInvite));
} catch (Exception e) {
return Optional.empty();
}
}
private AuthResult processUser(
String email,
String username,
String avatar,
com.openisle.model.RegisterMode mode,
boolean viaInvite
) {
Optional<User> existing = userRepository.findByEmail(email);
if (existing.isPresent()) {
User user = existing.get();
if (!user.isVerified()) {
user.setVerified(true);
user.setVerificationCode(null);
userRepository.save(user);
}
return new AuthResult(user, false);
}
String baseUsername = username != null ? username : email.split("@")[0];
String finalUsername = baseUsername;
int suffix = 1;
while (userRepository.findByUsername(finalUsername).isPresent()) {
finalUsername = baseUsername + suffix++;
}
User user = new User();
user.setUsername(finalUsername);
user.setEmail(email);
user.setPassword("");
user.setRole(Role.USER);
user.setVerified(true);
user.setApproved(mode == com.openisle.model.RegisterMode.DIRECT || viaInvite);
if (avatar != null) {
user.setAvatar(avatar);
} else {
user.setAvatar(avatarGenerator.generate(finalUsername));
}
return new AuthResult(userRepository.save(user), true);
}
}