Skip to content

Commit 14d26d5

Browse files
authored
Merge pull request #348 from lowcoder-org/add-ory-oauth-support
Add Feature To Include ORY As Possible Auth Provider
2 parents 730c161 + 2f34f66 commit 14d26d5

File tree

12 files changed

+240
-19
lines changed

12 files changed

+240
-19
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.lowcoder.sdk.auth;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonView;
5+
import lombok.Getter;
6+
import org.apache.commons.lang3.StringUtils;
7+
import org.lowcoder.sdk.auth.constants.AuthTypeConstants;
8+
import org.lowcoder.sdk.auth.constants.Oauth2Constants;
9+
import org.lowcoder.sdk.config.SerializeConfig.JsonViews;
10+
11+
import javax.annotation.Nullable;
12+
import java.util.function.Function;
13+
14+
import static org.lowcoder.sdk.auth.constants.Oauth2Constants.CLIENT_ID_PLACEHOLDER;
15+
import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER;
16+
17+
/**
18+
* OAuth2 ORY auth config.
19+
*/
20+
@Getter
21+
public class Oauth2OryAuthConfig extends Oauth2SimpleAuthConfig {
22+
23+
protected String instanceId;
24+
25+
@JsonCreator
26+
public Oauth2OryAuthConfig(
27+
@Nullable String id,
28+
Boolean enable,
29+
Boolean enableRegister,
30+
String source,
31+
String sourceName,
32+
String clientId,
33+
String clientSecret,
34+
String instanceId,
35+
String authType) {
36+
super(id, enable, enableRegister, source, sourceName, clientId, clientSecret, authType);
37+
this.instanceId = instanceId;
38+
}
39+
40+
@Override
41+
public String replaceAuthUrlClientIdPlaceholder(String url) {
42+
return super.replaceAuthUrlClientIdPlaceholder(url).replace(INSTANCE_ID_PLACEHOLDER, instanceId);
43+
}
44+
}

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2SimpleAuthConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public String getAuthorizeUrl() {
5252
return switch (authType) {
5353
case AuthTypeConstants.GOOGLE -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.GOOGLE_AUTHORIZE_URL);
5454
case AuthTypeConstants.GITHUB -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.GITHUB_AUTHORIZE_URL);
55+
case AuthTypeConstants.ORY -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.ORY_AUTHORIZE_URL);
5556
default -> null;
5657
};
5758
}
@@ -73,7 +74,7 @@ public void merge(AbstractAuthConfig oldConfig) {
7374
}
7475
}
7576

76-
private String replaceAuthUrlClientIdPlaceholder(String url) {
77+
public String replaceAuthUrlClientIdPlaceholder(String url) {
7778
return url.replace(CLIENT_ID_PLACEHOLDER, clientId);
7879
}
7980
}

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/AuthTypeConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ public class AuthTypeConstants {
88
public static final String FORM = "FORM";
99
public static final String GOOGLE = "GOOGLE";
1010
public static final String GITHUB = "GITHUB";
11+
public static final String ORY = "ORY";
1112
}

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ public class Oauth2Constants {
77
public static final String REDIRECT_URL_PLACEHOLDER = "$REDIRECT_URL";
88
public static final String STATE_PLACEHOLDER = "$STATE";
99

10+
public static final String INSTANCE_ID_PLACEHOLDER = "INSTANCE_ID";
11+
1012
// authorize url
1113
public static final String GITHUB_AUTHORIZE_URL = "https://github.com/login/oauth/authorize"
1214
+ "?response_type=code"
@@ -23,4 +25,11 @@ public class Oauth2Constants {
2325
+ "&access_type=offline"
2426
+ "&scope=openid email profile"
2527
+ "&prompt=select_account";
28+
29+
public static final String ORY_AUTHORIZE_URL = "https://" + INSTANCE_ID_PLACEHOLDER + "/oauth2/auth"
30+
+ "?response_type=code"
31+
+ "&client_id=" + CLIENT_ID_PLACEHOLDER
32+
+ "&redirect_uri=" + REDIRECT_URL_PLACEHOLDER
33+
+ "&state=" + STATE_PLACEHOLDER
34+
+ "&scope=openid email profile offline_access";
2635
}

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/AuthSourceConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ public class AuthSourceConstants {
1010
public static final String PHONE = "PHONE";
1111
public static final String GOOGLE = "GOOGLE";
1212
public static final String GITHUB = "GITHUB";
13+
public static final String ORY = "ORY";
1314

1415
// source name
1516
public static final String GOOGLE_NAME = "Google";
1617
public static final String GITHUB_NAME = "Github";
18+
public static final String ORY_NAME = "Ory";
1719

1820
// default source and source name for common protocol
1921
// oauth 2.0

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/JsonUtils.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package org.lowcoder.sdk.util;
22

3-
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.FORM;
4-
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.GITHUB;
5-
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.GOOGLE;
6-
73
import java.nio.charset.StandardCharsets;
84
import java.util.List;
95
import java.util.Map;
@@ -12,6 +8,7 @@
128
import javax.annotation.Nullable;
139

1410
import org.lowcoder.sdk.auth.EmailAuthConfig;
11+
import org.lowcoder.sdk.auth.Oauth2OryAuthConfig;
1512
import org.lowcoder.sdk.auth.Oauth2SimpleAuthConfig;
1613

1714
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -32,6 +29,8 @@
3229

3330
import lombok.extern.slf4j.Slf4j;
3431

32+
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.*;
33+
3534
@Slf4j
3635
public final class JsonUtils {
3736

@@ -46,6 +45,7 @@ public final class JsonUtils {
4645
OBJECT_MAPPER.registerSubtypes(new NamedType(EmailAuthConfig.class, FORM));
4746
OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2SimpleAuthConfig.class, GITHUB));
4847
OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2SimpleAuthConfig.class, GOOGLE));
48+
OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2OryAuthConfig.class, ORY));
4949
}
5050

5151
public static final JsonNode EMPTY_JSON_NODE = createObjectNode();

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/dto/AuthConfigRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public boolean isEnableRegister() {
2828
return MapUtils.getBoolean(this, "enableRegister", true);
2929
}
3030

31+
@Nullable
32+
public String getInstanceId() {
33+
return getString("instanceId");
34+
}
35+
3136
@Nullable
3237
public String getClientId() {
3338
return getString("clientId");

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2AuthRequestFactory.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
package org.lowcoder.api.authentication.request.oauth2;
22

3-
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.GITHUB;
4-
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.GOOGLE;
5-
63
import java.util.Set;
74

85
import org.lowcoder.api.authentication.request.AuthRequest;
96
import org.lowcoder.api.authentication.request.AuthRequestFactory;
107
import org.lowcoder.api.authentication.request.oauth2.request.AbstractOauth2Request;
118
import org.lowcoder.api.authentication.request.oauth2.request.GithubRequest;
129
import org.lowcoder.api.authentication.request.oauth2.request.GoogleRequest;
10+
import org.lowcoder.api.authentication.request.oauth2.request.OryRequest;
11+
import org.lowcoder.sdk.auth.Oauth2OryAuthConfig;
1312
import org.lowcoder.sdk.auth.Oauth2SimpleAuthConfig;
1413
import org.springframework.stereotype.Component;
1514

1615
import reactor.core.publisher.Mono;
1716

17+
import static org.lowcoder.sdk.auth.constants.AuthTypeConstants.*;
18+
1819
@Component
1920
public class Oauth2AuthRequestFactory implements AuthRequestFactory<OAuth2RequestContext> {
2021

@@ -27,6 +28,7 @@ private AbstractOauth2Request<? extends Oauth2SimpleAuthConfig> buildRequest(OAu
2728
return switch (context.getAuthConfig().getAuthType()) {
2829
case GITHUB -> new GithubRequest((Oauth2SimpleAuthConfig) context.getAuthConfig());
2930
case GOOGLE -> new GoogleRequest((Oauth2SimpleAuthConfig) context.getAuthConfig());
31+
case ORY -> new OryRequest((Oauth2OryAuthConfig) context.getAuthConfig());
3032
default -> throw new UnsupportedOperationException(context.getAuthConfig().getAuthType());
3133
};
3234
}
@@ -35,6 +37,7 @@ private AbstractOauth2Request<? extends Oauth2SimpleAuthConfig> buildRequest(OAu
3537
public Set<String> supportedAuthTypes() {
3638
return Set.of(
3739
GITHUB,
38-
GOOGLE);
40+
GOOGLE,
41+
ORY);
3942
}
4043
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.lowcoder.api.authentication.request.oauth2;
22

3+
import org.lowcoder.sdk.auth.constants.Oauth2Constants;
4+
35
public enum Oauth2DefaultSource implements Oauth2Source {
46

57
GITHUB {
@@ -35,5 +37,23 @@ public String refresh() {
3537
return "https://www.googleapis.com/oauth2/v4/token";
3638
}
3739

40+
},
41+
42+
ORY {
43+
@Override
44+
public String accessToken() {
45+
return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/oauth2/token";
46+
}
47+
48+
@Override
49+
public String userInfo() {
50+
return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/userinfo";
51+
}
52+
53+
@Override
54+
public String refresh() {
55+
return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/oauth2/token";
56+
}
57+
3858
}
3959
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package org.lowcoder.api.authentication.request.oauth2.request;
2+
3+
import org.apache.commons.collections4.MapUtils;
4+
import org.apache.http.client.utils.URIBuilder;
5+
import org.lowcoder.api.authentication.request.AuthException;
6+
import org.lowcoder.api.authentication.request.oauth2.OAuth2RequestContext;
7+
import org.lowcoder.api.authentication.request.oauth2.Oauth2DefaultSource;
8+
import org.lowcoder.domain.user.model.AuthToken;
9+
import org.lowcoder.domain.user.model.AuthUser;
10+
import org.lowcoder.sdk.auth.Oauth2OryAuthConfig;
11+
import org.lowcoder.sdk.util.JsonUtils;
12+
import org.lowcoder.sdk.webclient.WebClientBuildHelper;
13+
import org.springframework.core.ParameterizedTypeReference;
14+
import org.springframework.http.MediaType;
15+
import reactor.core.publisher.Mono;
16+
17+
import java.net.URI;
18+
import java.net.URISyntaxException;
19+
import java.util.Map;
20+
21+
import static org.springframework.web.reactive.function.BodyInserters.fromFormData;
22+
23+
public class OryRequest extends AbstractOauth2Request<Oauth2OryAuthConfig> {
24+
25+
public OryRequest(Oauth2OryAuthConfig config) {
26+
super(config, Oauth2DefaultSource.ORY);
27+
}
28+
29+
@Override
30+
protected Mono<AuthToken> getAuthToken(OAuth2RequestContext context) {
31+
URI uri;
32+
try {
33+
uri = new URIBuilder(config.replaceAuthUrlClientIdPlaceholder(source.accessToken())).build();
34+
} catch (URISyntaxException e) {
35+
throw new RuntimeException(e);
36+
}
37+
38+
return WebClientBuildHelper.builder()
39+
.systemProxy()
40+
.build()
41+
.post()
42+
.uri(uri)
43+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
44+
.body(fromFormData("code", context.getCode())
45+
.with("client_id", config.getClientId())
46+
.with("client_secret", config.getClientSecret())
47+
.with("grant_type", "authorization_code")
48+
.with("redirect_uri", context.getRedirectUrl()))
49+
.exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
50+
}))
51+
.flatMap(map -> {
52+
if (map.containsKey("error") || map.containsKey("error_description")) {
53+
throw new AuthException(JsonUtils.toJson(map));
54+
}
55+
AuthToken authToken = AuthToken.builder()
56+
.accessToken(MapUtils.getString(map, "access_token"))
57+
.expireIn(MapUtils.getIntValue(map, "expires_in"))
58+
.refreshToken(MapUtils.getString(map, "refresh_token"))
59+
.build();
60+
return Mono.just(authToken);
61+
});
62+
}
63+
64+
@Override
65+
protected Mono<AuthToken> refreshAuthToken(String refreshToken) {
66+
67+
URI uri;
68+
try {
69+
uri = new URIBuilder(config.replaceAuthUrlClientIdPlaceholder(source.refresh())).build();
70+
} catch (URISyntaxException e) {
71+
throw new RuntimeException(e);
72+
}
73+
74+
return WebClientBuildHelper.builder()
75+
.systemProxy()
76+
.build()
77+
.post()
78+
.uri(uri)
79+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
80+
.body(fromFormData("refresh_token", refreshToken)
81+
.with("client_id", config.getClientId())
82+
.with("client_secret", config.getClientSecret())
83+
.with("grant_type", "refresh_token"))
84+
.exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
85+
}))
86+
.flatMap(map -> {
87+
if (map.containsKey("error") || map.containsKey("error_description")) {
88+
throw new AuthException(JsonUtils.toJson(map));
89+
}
90+
AuthToken authToken = AuthToken.builder()
91+
.accessToken(MapUtils.getString(map, "access_token"))
92+
.expireIn(MapUtils.getIntValue(map, "expires_in"))
93+
.refreshToken(MapUtils.getString(map, "refresh_token"))
94+
.build();
95+
return Mono.just(authToken);
96+
});
97+
98+
}
99+
100+
@Override
101+
protected Mono<AuthUser> getAuthUser(AuthToken authToken) {
102+
return WebClientBuildHelper.builder()
103+
.systemProxy()
104+
.build()
105+
.post()
106+
.uri(config.replaceAuthUrlClientIdPlaceholder(source.userInfo()))
107+
.header("Authorization", "Bearer " + authToken.getAccessToken())
108+
.exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
109+
}))
110+
.flatMap(map -> {
111+
if (map.containsKey("error") || map.containsKey("error_description")) {
112+
throw new AuthException(JsonUtils.toJson(map));
113+
}
114+
AuthUser authUser = AuthUser.builder()
115+
.uid(MapUtils.getString(map, "sub"))
116+
.username(MapUtils.getString(map, "name"))
117+
.avatar(MapUtils.getString(map, "picture"))
118+
.rawUserInfo(map)
119+
.build();
120+
return Mono.just(authUser);
121+
});
122+
}
123+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy