Skip to content

Commit

Permalink
feat(server): make auth compatible with OpenAi clients
Browse files Browse the repository at this point in the history
  • Loading branch information
astappiev committed Jun 10, 2024
1 parent 159eb8d commit d7995d8
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import java.util.Optional;
import java.util.Set;

import de.l3s.interweb.server.features.user.Token;

import io.quarkus.smallrye.jwt.runtime.auth.JWTAuthMechanism;

import jakarta.enterprise.context.ApplicationScoped;

import io.netty.handler.codec.http.HttpResponseStatus;
Expand All @@ -18,11 +22,20 @@

@ApplicationScoped
public class ApiKeyAuthMechanism implements HttpAuthenticationMechanism {
public static final AsciiString API_KEY = AsciiString.cached("Api-Key");
public static final AsciiString APIKEY_HEADER = AsciiString.cached("Api-Key");
public static final AsciiString AUTHORIZATION_HEADER = AsciiString.cached("Authorization");
public static final int AUTHORIZATION_HEADER_PREFIX_LENGTH = JWTAuthMechanism.BEARER.length() + 1;
public static final int AUTHORIZATION_HEADER_LENGTH = AUTHORIZATION_HEADER_PREFIX_LENGTH + Token.TOKEN_LENGTH;

@Override
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
String authHeader = context.request().headers().get(API_KEY);
String authHeader = context.request().headers().get(APIKEY_HEADER);
if (authHeader == null) {
authHeader = context.request().headers().get(AUTHORIZATION_HEADER);
if (authHeader != null && authHeader.length() == AUTHORIZATION_HEADER_LENGTH) {
authHeader = authHeader.substring(AUTHORIZATION_HEADER_PREFIX_LENGTH);
}
}
if (authHeader != null) {
context.put(HttpAuthenticationMechanism.class.getName(), this);
return identityProviderManager.authenticate(HttpSecurityUtils.setRoutingContextAttribute(new ApiKeyAuthenticationRequest(authHeader), context));
Expand All @@ -32,12 +45,12 @@ public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProvid

@Override
public Uni<ChallengeData> getChallenge(RoutingContext context) {
String authHeader = context.request().headers().get(API_KEY);
String authHeader = context.request().headers().get(APIKEY_HEADER);
if (authHeader == null) {
return Uni.createFrom().optional(Optional.empty());
}

ChallengeData result = new ChallengeData(HttpResponseStatus.UNAUTHORIZED.code(), API_KEY, "");
ChallengeData result = new ChallengeData(HttpResponseStatus.UNAUTHORIZED.code(), APIKEY_HEADER, "");
return Uni.createFrom().item(result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.AuthenticationRequest;
import io.quarkus.security.identity.request.TokenAuthenticationRequest;
import io.quarkus.smallrye.jwt.runtime.auth.JWTAuthMechanism;
import io.quarkus.vertx.http.runtime.security.ChallengeData;
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
import io.quarkus.vertx.http.runtime.security.HttpCredentialTransport;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

Expand All @@ -30,18 +30,33 @@ public class HybridAuthMechanism implements HttpAuthenticationMechanism {

@Override
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
return apiKeyMechanism.authenticate(context, identityProviderManager)
.onItem().ifNull().switchTo(() -> jwtMechanism.authenticate(context, identityProviderManager));
return selectBetweenJwtAndOidc(context).authenticate(context, identityProviderManager);
}

@Override
public Uni<ChallengeData> getChallenge(RoutingContext context) {
return apiKeyMechanism.getChallenge(context)
.onItem().ifNull().switchTo(() -> jwtMechanism.getChallenge(context));
return jwtMechanism.getChallenge(context);
}

@Override
public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
return Set.of(TokenAuthenticationRequest.class, ApiKeyAuthenticationRequest.class);
return jwtMechanism.getCredentialTypes();
}

@Override
public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
return jwtMechanism.getCredentialTransport(context);
}

private HttpAuthenticationMechanism selectBetweenJwtAndOidc(RoutingContext context) {
String apikeyHeader = context.request().headers().get(ApiKeyAuthMechanism.APIKEY_HEADER);
if (apikeyHeader != null) {
return apiKeyMechanism;
}
String authHeader = context.request().headers().get(ApiKeyAuthMechanism.AUTHORIZATION_HEADER);
if (authHeader != null && authHeader.length() == ApiKeyAuthMechanism.AUTHORIZATION_HEADER_LENGTH) {
return apiKeyMechanism;
}
return jwtMechanism;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
@Cacheable
@Table(name = "user_token")
public class Token extends PanacheEntityBase implements Credential {
public static final int TOKEN_LENGTH = 64;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
Expand All @@ -39,7 +41,7 @@ public class Token extends PanacheEntityBase implements Credential {

@NotEmpty
@NotNull
@Column(unique = true, length = 64)
@Column(unique = true, length = TOKEN_LENGTH)
public String apikey;

public Token() {
Expand All @@ -48,7 +50,7 @@ public Token() {

public static Token generate() {
Token token = new Token();
token.apikey = StringUtils.randomAlphanumeric(64);
token.apikey = StringUtils.randomAlphanumeric(TOKEN_LENGTH);
return token;
}

Expand Down
28 changes: 28 additions & 0 deletions interweb-server/src/test/python/openai_client.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import httpx
from openai import OpenAI

client = OpenAI(
base_url="http://localhost:8080",
api_key=os.getenv("INTERWEB_APIKEY"),
http_client=httpx.Client(proxy="http://localhost:8000")
)

message_text = [{
"role": "system",
"content": "You are an AI assistant that helps people find information."
}]

completion = client.chat.completions.create(
model="gpt-35-turbo",
messages=message_text,
temperature=0.7,
max_tokens=800,
top_p=0.95,
frequency_penalty=0,
presence_penalty=0,
stop=None
)

print('user:', message_text[0]['content'])
print('assistant:', completion.choices[0].message.content)

0 comments on commit d7995d8

Please sign in to comment.