Skip to content

Commit 2bcd8be

Browse files
committed
feat: add node ace add to solvro auth
1 parent d0e0852 commit 2bcd8be

File tree

16 files changed

+232
-137
lines changed

16 files changed

+232
-137
lines changed

.keycloakify/realm-kc-26.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,14 +1718,14 @@
17181718
"subComponents": {},
17191719
"config": {
17201720
"allowed-protocol-mapper-types": [
1721-
"oidc-full-name-mapper",
1722-
"oidc-address-mapper",
17231721
"saml-user-attribute-mapper",
1724-
"saml-user-property-mapper",
1725-
"saml-role-list-mapper",
17261722
"oidc-sha256-pairwise-sub-mapper",
1727-
"oidc-usermodel-attribute-mapper",
1728-
"oidc-usermodel-property-mapper"
1723+
"oidc-address-mapper",
1724+
"saml-role-list-mapper",
1725+
"oidc-usermodel-property-mapper",
1726+
"oidc-full-name-mapper",
1727+
"saml-user-property-mapper",
1728+
"oidc-usermodel-attribute-mapper"
17291729
]
17301730
}
17311731
},
@@ -1755,14 +1755,14 @@
17551755
"subComponents": {},
17561756
"config": {
17571757
"allowed-protocol-mapper-types": [
1758-
"saml-user-attribute-mapper",
1759-
"saml-user-property-mapper",
1760-
"oidc-sha256-pairwise-sub-mapper",
17611758
"oidc-address-mapper",
1762-
"saml-role-list-mapper",
17631759
"oidc-usermodel-property-mapper",
1760+
"oidc-full-name-mapper",
1761+
"saml-user-property-mapper",
17641762
"oidc-usermodel-attribute-mapper",
1765-
"oidc-full-name-mapper"
1763+
"saml-user-attribute-mapper",
1764+
"saml-role-list-mapper",
1765+
"oidc-sha256-pairwise-sub-mapper"
17661766
]
17671767
}
17681768
},
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import assert from "node:assert";
2+
3+
import type { HttpContext } from "@adonisjs/core/http";
4+
5+
import User from "#models/user";
6+
7+
export default class AuthController {
8+
async login(ctx: HttpContext): Promise<void> {
9+
const driver = ctx.ally.use("solvroAuth");
10+
11+
return driver.redirect();
12+
}
13+
14+
async callback(ctx: HttpContext): Promise<void> {
15+
const driver = ctx.ally.use("solvroAuth");
16+
17+
const details = await driver.user();
18+
19+
assert(details.email !== null, "Invalid user profile. Email is missing");
20+
21+
const user = await User.firstOrCreate(
22+
{ email: details.email },
23+
{
24+
email: details.email,
25+
fullName: details.name,
26+
},
27+
);
28+
29+
await ctx.auth.use("web").login(user);
30+
31+
return ctx.response.redirect("/");
32+
}
33+
34+
async logout(ctx: HttpContext): Promise<void> {
35+
await ctx.auth.use("web").logout();
36+
37+
return ctx.response.redirect("/");
38+
}
39+
}

examples/adonisjs/app/models/user.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
import { DateTime } from "luxon";
22

3-
import { withAuthFinder } from "@adonisjs/auth/mixins/lucid";
4-
import { compose } from "@adonisjs/core/helpers";
5-
import hash from "@adonisjs/core/services/hash";
63
import { BaseModel, column } from "@adonisjs/lucid/orm";
74

8-
const AuthFinder = withAuthFinder(() => hash.use("scrypt"), {
9-
uids: ["email"],
10-
passwordColumnName: "password",
11-
});
12-
13-
export default class User extends compose(BaseModel, AuthFinder) {
5+
export default class User extends BaseModel {
146
@column({ isPrimary: true })
157
declare id: number;
168

examples/adonisjs/config/ally.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ import { defineConfig } from "@adonisjs/ally";
22

33
import env from "#start/env";
44

5-
import { KeycloakDriverService } from "../../../packages/ally-solvro-auth/build/index.js";
5+
import { SolvroAuthDriverService } from "../../../packages/ally-solvro-auth/build/index.js";
66

77
const allyConfig = defineConfig({
8-
keycloak: KeycloakDriverService({
9-
callbackUrl: "http://localhost:3333/auth/keycloak/callback",
10-
clientId: env.get("KEYCLOAK_CLIENT_ID", "myclient"),
11-
clientSecret: env.get("KEYCLOAK_CLIENT_SECRET", ""),
12-
keycloakUrl:
13-
"http://localhost:8080/realms/{realm}/protocol/openid-connect/{action}",
14-
realm: env.get("KEYCLOAK_REALM", "myrealm"),
8+
solvroAuth: SolvroAuthDriverService({
9+
callbackUrl: `${env.get("APP_DOMAIN")}/auth/callback`,
10+
clientId: env.get("SOLVRO_AUTH_CLIENT_ID", "myclient"),
11+
clientSecret: env.get("SOLVRO_AUTH_CLIENT_SECRET", ""),
12+
solvroAuthUrl: "http://localhost:8080",
1513
}),
1614
});
1715

examples/adonisjs/database/migrations/1739284529117_create_users_table.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default class extends BaseSchema {
88
table.increments("id").notNullable();
99
table.string("full_name").nullable();
1010
table.string("email", 254).notNullable().unique();
11-
table.string("password").notNullable();
1211

1312
table.timestamp("created_at").notNullable();
1413
table.timestamp("updated_at").nullable();

examples/adonisjs/start/env.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default await Env.create(new URL("../", import.meta.url), {
1414
NODE_ENV: Env.schema.enum(["development", "production", "test"] as const),
1515
PORT: Env.schema.number(),
1616
APP_KEY: Env.schema.string(),
17+
APP_DOMAIN: Env.schema.string(),
1718
HOST: Env.schema.string({ format: "host" }),
1819
LOG_LEVEL: Env.schema.enum([
1920
"fatal",
@@ -31,6 +32,6 @@ export default await Env.create(new URL("../", import.meta.url), {
3132
*/
3233
SESSION_DRIVER: Env.schema.enum(["cookie", "memory"] as const),
3334

34-
KEYCLOAK_CLIENT_ID: Env.schema.string(),
35-
KEYCLOAK_CLIENT_SECRET: Env.schema.string.optional(),
35+
SOLVRO_AUTH_CLIENT_ID: Env.schema.string(),
36+
SOLVRO_AUTH_CLIENT_SECRET: Env.schema.string.optional(),
3637
});

examples/adonisjs/start/kernel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ router.use([
4444
export const middleware = router.named({
4545
guest: () => import("#middleware/guest_middleware"),
4646
auth: () => import("#middleware/auth_middleware"),
47+
silentAuth: () => import("#middleware/silent_auth_middleware"),
4748
});

examples/adonisjs/start/routes.ts

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,21 @@
88
*/
99
import router from "@adonisjs/core/services/router";
1010

11-
import User from "#models/user";
12-
13-
router.get("/auth/keycloak/redirect", ({ ally }) => {
14-
return ally.use("keycloak").redirect();
15-
});
16-
17-
router.get("/auth/keycloak/callback", async ({ ally, auth }) => {
18-
const keycloak = ally.use("keycloak");
19-
/**
20-
* User has denied access by canceling
21-
* the login flow
22-
*/
23-
if (keycloak.accessDenied()) {
24-
return "You have cancelled the login process";
25-
}
26-
27-
/**
28-
* OAuth state verification failed. This happens when the
29-
* CSRF cookie gets expired.
30-
*/
31-
if (keycloak.stateMisMatch()) {
32-
return "We are unable to verify the request. Please try again";
33-
}
34-
35-
/**
36-
* GitHub responded with some error
37-
*/
38-
if (keycloak.hasError()) {
39-
return keycloak.getError();
40-
}
41-
42-
/**
43-
* Access user info
44-
*/
45-
const keycloakUser = await keycloak.user();
46-
47-
const user = await User.firstOrCreate(
48-
{ email: keycloakUser.email },
49-
{
50-
email: keycloakUser.email,
51-
},
52-
);
53-
54-
auth.use("web").login();
55-
return user;
56-
});
57-
58-
router.get("/", async () => {
59-
return {
60-
hello: "world",
61-
};
62-
});
11+
import { middleware } from "./kernel.js";
12+
13+
const AuthController = () => import("#controllers/auth_controller");
14+
15+
router.get("/auth/login", [AuthController, "login"]).use(middleware.guest());
16+
router
17+
.get("/auth/callback", [AuthController, "callback"])
18+
.use(middleware.guest());
19+
router.post("/auth/logout", [AuthController, "logout"]).use(middleware.auth());
20+
21+
router
22+
.get("/", async ({ auth }) => {
23+
return {
24+
hello: "world",
25+
user: auth.user,
26+
};
27+
})
28+
.use(middleware.silentAuth());
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @rlanz/bull-queue
3+
*
4+
* @license MIT
5+
* @copyright Romain Lanz <[email protected]>
6+
*/
7+
import type Configure from "@adonisjs/core/commands/configure";
8+
9+
import { stubsRoot } from "./stubs/main.js";
10+
11+
export async function configure(command: Configure) {
12+
const codemods = await command.createCodemods();
13+
14+
await codemods.makeUsingStub(
15+
stubsRoot,
16+
"controllers/auth_controller.stub",
17+
{},
18+
);
19+
// env variables: APP_DOMAIN, SOLVRO_AUTH_CLIENT_ID
20+
// Add environment variables
21+
await codemods.defineEnvVariables({
22+
APP_DOMAIN: "http://localhost:3333",
23+
SOLVRO_AUTH_CLIENT_ID: "MY_CLIENT_ID",
24+
});
25+
26+
await codemods.defineEnvValidations({
27+
variables: {
28+
APP_DOMAIN: `Env.schema.string()`,
29+
SOLVRO_AUTH_CLIENT_ID: "Env.schema.string()",
30+
},
31+
leadingComment: "Variables for @solvro/auth",
32+
});
33+
}

packages/ally-solvro-auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./src/driver.js";
2+
export { configure } from "./configure.js";

0 commit comments

Comments
 (0)