Skip to content

Commit

Permalink
Merge pull request #502 from curveball/first-party-auth
Browse files Browse the repository at this point in the history
First attempt at implementing 'OAuth2 for first party apps'
  • Loading branch information
evert authored Jul 30, 2024
2 parents cf4217e + bfd9831 commit 830e0b9
Show file tree
Hide file tree
Showing 38 changed files with 800 additions and 189 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -41,7 +41,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -63,7 +63,7 @@ jobs:
ports:
- 3306:3306
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -89,7 +89,7 @@ jobs:
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -108,7 +108,7 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Changelog
* API clients can now request that one-time-tokens don't expire after use.
* The client_id is now validated to belong to the curent user when validating
one-time-tokens.
* Fixed result of one-time-token if a custom expiry was used.


0.25.0 (2023-11-22)
Expand Down
27 changes: 27 additions & 0 deletions schemas/authorization-challenge-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://curveballjs.org/schemas/a12nserver/authorization-challenge-request.json",
"type": "object",
"title": "AuthorizationChallengeRequest",
"description": "Request body for the Authorization Challenge Request endpoint for first-party applications.",
"required": [],
"additionalProperties": false,

"properties": {
"scope": {
"type": "string",
"description": "OAuth2 scope"
},
"auth_session": {
"description": "If the client has started a login session, specify auth_session to continue the login process",
"type": "string"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}

}
22 changes: 22 additions & 0 deletions src/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ export interface App {
* and run json-schema-to-typescript to regenerate this file.
*/

/**
* Request body for the Authorization Challenge Request endpoint for first-party applications.
*/
export interface AuthorizationChallengeRequest {
/**
* OAuth2 scope
*/
scope?: string;
/**
* If the client has started a login session, specify auth_session to continue the login process
*/
auth_session?: string;
username?: string;
password?: string;
}
/* eslint-disable */
/**
* This file was automatically generated by json-schema-to-typescript.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run json-schema-to-typescript to regenerate this file.
*/

/**
* This is the request body used by the HTML form submission for creating new groups
*/
Expand Down
9 changes: 6 additions & 3 deletions src/app-client/controller/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Forbidden, UnprocessableContent } from '@curveball/http-errors';

import * as hal from '../formats/hal.js';
import { PrincipalService } from '../../principal/service.js';
import { GrantType, OAuth2Client } from '../../types.js';
import { GrantType, AppClient } from '../../types.js';
import { findByApp, create } from '../service.js';
import { generatePublicId, generateSecretToken } from '../../crypto.js';

Expand Down Expand Up @@ -39,7 +39,7 @@ class ClientCollectionController extends Controller {
if (ctx.request.body.allowClientCredentials) {
allowedGrantTypes.push('client_credentials');
}
if (ctx.request.body.allowAuthorizationCode) {
if (ctx.request.body.allowAuthorizationCode || ctx.request.body.allowAuthorizationChallenge) {
allowedGrantTypes.push('authorization_code');
}
if (ctx.request.body.allowImplicit) {
Expand All @@ -51,6 +51,9 @@ class ClientCollectionController extends Controller {
if (ctx.request.body.allowPassword) {
allowedGrantTypes.push('password');
}
if (ctx.request.body.allowAuthorizationChallenge) {
allowedGrantTypes.push('authorization_challenge');
}

let clientId = ctx.request.body.clientId;

Expand All @@ -67,7 +70,7 @@ class ClientCollectionController extends Controller {
}

const clientSecret = `secret-token:${await generateSecretToken()}`;
const newClient: Omit<OAuth2Client,'id'|'href'> = {
const newClient: Omit<AppClient,'id'|'href'> = {
clientId,
app,
allowedGrantTypes: allowedGrantTypes,
Expand Down
5 changes: 4 additions & 1 deletion src/app-client/controller/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class EditClientController extends Controller {
if (ctx.request.body.allowClientCredentials) {
allowedGrantTypes.push('client_credentials');
}
if (ctx.request.body.allowAuthorizationCode) {
if (ctx.request.body.allowAuthorizationCode || ctx.request.body.allowAuthorizationChallenge) {
allowedGrantTypes.push('authorization_code');
}
if (ctx.request.body.allowImplicit) {
Expand All @@ -55,6 +55,9 @@ class EditClientController extends Controller {
if (ctx.request.body.allowPassword) {
allowedGrantTypes.push('password');
}
if (ctx.request.body.allowAuthorizationChallenge) {
allowedGrantTypes.push('authorization_challenge');
}

const redirectUris = ctx.request.body.redirectUris.trim().split(/\r\n|\n/).filter((line:string) => !!line);

Expand Down
16 changes: 11 additions & 5 deletions src/app-client/formats/hal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { App, OAuth2Client } from '../../types.js';
import { App, AppClient } from '../../types.js';
import { HalResource } from 'hal-types';

export function collection(app: App, clients: OAuth2Client[]): HalResource {
export function collection(app: App, clients: AppClient[]): HalResource {

return {
_links: {
Expand Down Expand Up @@ -44,7 +44,7 @@ export function collection(app: App, clients: OAuth2Client[]): HalResource {
};

}
export function item(client: OAuth2Client, redirectUris: string[]): HalResource {
export function item(client: AppClient, redirectUris: string[]): HalResource {

return {
_links: {
Expand All @@ -62,7 +62,7 @@ export function item(client: OAuth2Client, redirectUris: string[]): HalResource
};

}
export function editForm(client: OAuth2Client, redirectUris: string[]): HalResource {
export function editForm(client: AppClient, redirectUris: string[]): HalResource {

return {
_links: {
Expand Down Expand Up @@ -109,6 +109,12 @@ export function editForm(client: OAuth2Client, redirectUris: string[]): HalResou
type: 'checkbox',
value: client.allowedGrantTypes.includes('implicit') ? 'true' : '',
},
{
name: 'allowAuthorizationChallenge',
prompt: 'Allow "OAuth 2.0 Authorization Challenge for First-Party applications" flow (experimental and only for trusted applications!) (implies authorization_code)',
type: 'checkbox',
value: client.allowedGrantTypes.includes('authorization_challenge') ? 'true' :'',
},
{
name: 'allowRefreshToken',
prompt: 'Allow "refresh_token" grant_type',
Expand All @@ -134,7 +140,7 @@ export function editForm(client: OAuth2Client, redirectUris: string[]): HalResou

}

export function newClientSuccess(client: OAuth2Client, redirectUris: string[] ,secret: string): HalResource {
export function newClientSuccess(client: AppClient, redirectUris: string[] ,secret: string): HalResource {

return {
_links: {
Expand Down
10 changes: 8 additions & 2 deletions src/app-client/formats/siren.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,19 @@ export function newClient(user: App, query: NewClientQuery ) {
},
{
name: 'allowPassword',
title: 'Allow "password" grant_type (trusted applications only)',
title: 'Allow "password" grant_type (trusted applications only!)',
type: 'checkbox',
value: allowedGrantTypes.includes('password')
},
{
name: 'allowAuthorizationChallenge',
title: 'Allow "OAuth 2.0 Authorization Challenge for First-Party applications" flow (experimental and only for trusted applications!)',
type: 'checkbox',
value: allowedGrantTypes.includes('allowAuthorizationChallenge')
},
{
name: 'allowImplicit',
title: 'Allow "implicit" grant_type (deprecated) ',
title: 'Allow "implicit" grant_type (deprecated! insecure!) ',
type: 'checkbox',
value: allowedGrantTypes.includes('implicit')
},
Expand Down
Loading

0 comments on commit 830e0b9

Please sign in to comment.