-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,596 additions
and
1,635 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,26 @@ | ||
### Auth action | ||
|
||
```ts | ||
import * as HttpErrors from 'http-errors'; | ||
|
||
async action(request: Request): Promise<UserProfile | undefined> { | ||
const strategy = await this.getStrategy(); | ||
if (!strategy) { | ||
const authStrategy = await this.getAuthStrategy(); | ||
if (!authStrategy) { | ||
// The invoked operation does not require authentication. | ||
return undefined; | ||
} | ||
|
||
// read the action metadata from endpoint's auth metadata like | ||
// `@authenticate('strategy_name', {action: 'login'})` | ||
// type ActionType = 'login' | 'verify' | ||
const action = await this.getAction(); | ||
if (!action) { | ||
throw new Error('no action specified for your endpoint') | ||
} | ||
if (!strategy[action]) { | ||
throw new Error('invalid strategy parameter'); | ||
} | ||
|
||
let user: UserProfile; | ||
try { | ||
switch(action) { | ||
case 'login': user = strategy.login(request); | ||
case 'verify': user = strategy.verify(request); | ||
default: return; | ||
const userProfile: UserProfile = await authStrategy.authenticate(request); | ||
this.setCurrentUser(userProfile); | ||
// a convenient return for the next request handlers | ||
return userProfile; | ||
} catch (err) { | ||
// interpret the raw error code/msg here and throw the corresponding HTTP error | ||
// convert it to http error | ||
if (err.code == '401') { | ||
throw new HttpErrors.Unauthorized(err.message); | ||
} | ||
} catch(err) { | ||
if (err) throw err; | ||
} | ||
|
||
this.setCurrentUser(user); | ||
return user; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,17 @@ | ||
### Auth strategy class | ||
|
||
There are two flavors of defining the strategy interface. One is having only one function `authenticate` which takes in the action type and handles login and verify accordingly. The other one is having two functions `login` and `verify`. | ||
|
||
After having the sync up meeting with Raymond we agreed on flavor #1. While when we were writing the pseudo code for strategies + services, we felt the word `authenticate` is very misleading and switched back to having two separate functions to distinguish between the login and verify flow. | ||
|
||
A summary of the pros/cons of each flavor will be provided later. Discussion/feedback are welcomed. Currently this PR uses flavor #2. | ||
|
||
- flavor 1 | ||
|
||
```ts | ||
|
||
type ActionType = 'login' | 'verify'; | ||
|
||
class AuthticationStrategy { | ||
authenticate(option: {action: ActionType}): Promise<UserProfile | undefined> { | ||
// 1. Try to find current user | ||
// 2. If found, return it | ||
// 3. If not found: | ||
// 3.1 if /login invokes this action, | ||
// performs login using this particular strategy | ||
// 3.2 if other API invokes this action, then throw 401 error | ||
}; | ||
} | ||
``` | ||
|
||
- flavor 2 | ||
|
||
```ts | ||
class AuthticationStrategy { | ||
login(request, response): Promise<UserProfile | undefined> {}; | ||
|
||
verify(request): Promise<UserProfile | undefined> { | ||
// 1. Try to find current user | ||
// 2. If found, return it | ||
// 3. If not found, throw 401: | ||
} | ||
import {Request} from '@loopback/rest'; | ||
|
||
interface AuthenticationStrategy { | ||
// The resolver will read the options object from metadata, call `strategy.setOptions` | ||
options: object; | ||
authenticate(request: Request): Promise<UserProfile | undefined>; | ||
setOptions(options: object); | ||
// This is a private function that extracts credential fields from a request, | ||
// it is called in function `authenticate`. You could organize the extraction | ||
// logic in this function or write them in `authenticate` directly without defining | ||
// this extra utility. | ||
private extractCredentials?(request: Request): Promise<Credentials>; | ||
} | ||
``` |
Oops, something went wrong.