Skip to content

Commit 5cc0f43

Browse files
committed
add extra fields support to oAuth AccessTokenResponse
Signed-off-by: Laurent ARNAL <laurent@clae.net>
1 parent e17886b commit 5cc0f43

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

bundles/org.openhab.core.auth.oauth2client/src/main/java/org/openhab/core/auth/oauth2client/internal/OAuthConnector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.eclipse.jetty.http.HttpStatus;
3838
import org.eclipse.jetty.util.Fields;
3939
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
40+
import org.openhab.core.auth.client.oauth2.AccessTokenResponseExtraFieldsAdapterFactory;
4041
import org.openhab.core.auth.client.oauth2.OAuthException;
4142
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
4243
import org.openhab.core.io.net.http.HttpClientFactory;
@@ -120,7 +121,7 @@ public OAuthConnector(HttpClientFactory httpClientFactory, @Nullable Fields extr
120121
} catch (DateTimeParseException e) {
121122
return LocalDateTime.parse(json.getAsString()).atZone(ZoneId.systemDefault()).toInstant();
122123
}
123-
}).create();
124+
}).registerTypeAdapterFactory(new AccessTokenResponseExtraFieldsAdapterFactory()).create();
124125
}
125126

126127
/**

bundles/org.openhab.core/src/main/java/org/openhab/core/auth/client/oauth2/AccessTokenResponse.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.io.Serial;
1616
import java.io.Serializable;
1717
import java.time.Instant;
18+
import java.util.Collections;
19+
import java.util.Map;
1820
import java.util.Objects;
1921

2022
/**
@@ -102,6 +104,21 @@ public final class AccessTokenResponse implements Serializable, Cloneable {
102104
*/
103105
private Instant createdOn;
104106

107+
/**
108+
* Extra element that possibly pass in the token by specific oAuth implementation
109+
*
110+
*
111+
*/
112+
private Map<String, String> extraFields = Collections.emptyMap();
113+
114+
public Map<String, String> getExtraFields() {
115+
return extraFields;
116+
}
117+
118+
public void setExtraFields(Map<String, String> extraFields) {
119+
this.extraFields = extraFields;
120+
}
121+
105122
/**
106123
* Calculate if the token is expired against the given time.
107124
* It also returns true even if the token is not initialized (i.e. object newly created).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.core.auth.client.oauth2;
14+
15+
import java.io.IOException;
16+
import java.util.HashMap;
17+
import java.util.Map;
18+
import java.util.Set;
19+
20+
import com.google.gson.Gson;
21+
import com.google.gson.JsonElement;
22+
import com.google.gson.JsonObject;
23+
import com.google.gson.JsonPrimitive;
24+
import com.google.gson.TypeAdapter;
25+
import com.google.gson.TypeAdapterFactory;
26+
import com.google.gson.reflect.TypeToken;
27+
import com.google.gson.stream.JsonReader;
28+
import com.google.gson.stream.JsonWriter;
29+
30+
public final class AccessTokenResponseExtraFieldsAdapterFactory implements TypeAdapterFactory {
31+
32+
private static final Set<String> KNOWN_FIELDS = Set.of("access_token", "token_type", "expires_in", "refresh_token",
33+
"scope", "state");
34+
35+
@Override
36+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
37+
if (!AccessTokenResponse.class.isAssignableFrom(type.getRawType())) {
38+
return null;
39+
}
40+
41+
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
42+
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
43+
44+
return new TypeAdapter<>() {
45+
46+
@Override
47+
public void write(JsonWriter out, T value) throws IOException {
48+
delegate.write(out, value);
49+
}
50+
51+
@Override
52+
public T read(JsonReader in) throws IOException {
53+
JsonElement tree = elementAdapter.read(in);
54+
if (tree != null) {
55+
JsonObject obj = tree.getAsJsonObject();
56+
57+
T parsed = delegate.fromJsonTree(tree);
58+
AccessTokenResponse response = (AccessTokenResponse) parsed;
59+
60+
Map<String, String> extras = new HashMap<>();
61+
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
62+
String key = entry.getKey();
63+
if (KNOWN_FIELDS.contains(key)) {
64+
continue;
65+
}
66+
extras.put(key, toStringValue(gson, entry.getValue()));
67+
}
68+
69+
if (response != null) {
70+
response.setExtraFields(extras);
71+
}
72+
return parsed;
73+
}
74+
return null;
75+
}
76+
};
77+
}
78+
79+
private static String toStringValue(Gson gson, JsonElement el) {
80+
if (el == null || el.isJsonNull()) {
81+
return null;
82+
}
83+
if (el.isJsonPrimitive()) {
84+
JsonPrimitive p = el.getAsJsonPrimitive();
85+
return p.getAsString();
86+
}
87+
88+
return gson.toJson(el);
89+
}
90+
}

0 commit comments

Comments
 (0)