Goals:
- Provide a way to specify authentication at the operation level microsoft/typespec#1210
- Provide a way to specify the scope for OAuth2(and any other auth scheme) microsoft/typespec#1652
- Allow
@useAuth
on all operations, interace, etc. - Using
@useAuth
at a lower level override the value at the higher level. - To specify scopes for a given operation specify different scope to the oauth2 scheme
alias ServiceKeyAuth = ApiKeyAuth<ApiKeyLocation.header, "X-API-KEY">;
@useAuth(ServiceKeyAuth)
namespace MyService;
// Have to use X-API-KEY header auth.
op list(): FileInfo[];
// Could use
// - X-API-KEY header
// - token query
// - no auth
@useAuth(ServiceKeyAuth | ApiKeyAuth<ApiKeyLocation.query, "token"> | NoAuth)
op download(fileId: string): bytes;
alias ServiceKeyAuth = ApiKeyAuth<ApiKeyLocation.header, "X-API-KEY">;
@useAuth(ServiceKeyAuth)
namespace MyService;
// Have to use X-API-KEY header auth.
op list(): FileInfo[];
@useAuth(ServiceKeyAuth | ApiKeyAuth<ApiKeyLocation.query, "token"> | NoAuth)
interface FileManagement {
// Could use
// - X-API-KEY header
// - token query
// - no auth
op download(fileId: string): bytes;
// Could use
// - X-API-KEY header
// - token query
// - no auth
op upload(fileId: string, data: bytes): void;
}
alias ServiceFlow<T extends string[]> = {
type: OAuth2FlowType.implicit;
authorizationUrl: "https://api.example.com/oauth2/authorize";
refreshUrl: "https://api.example.com/oauth2/refresh";
scopes: T;
}
alias ServiceOAuth<T extends string[]> = OAuth2<[ServiceFlow<T>]>;
@useAuth(ServiceKeyAuth<["read", "write", "delete"]>)
namespace MyService;
// Have to use X-API-KEY header auth.
@useAuth(ServiceOAuth<["read"]>)
op list(): FileInfo[];
@useAuth(ServiceOAuth<["read"]>)
op read(): FileInfo;
@useAuth(ServiceOAuth<["write"]>)
op upload();
@useAuth(ServiceOAuth<["delete"]>)
op delete();
Pros:
- works well with the current system
- not breaking
Cons:
- This is needs a decent amount of boilerplate.
- Resolving the scope for each operation might not be that simple in the code
Detach the scopes from the flow
alias ServiceFlow = {
type: OAuth2FlowType.implicit;
authorizationUrl: "https://api.example.com/oauth2/authorize";
refreshUrl: "https://api.example.com/oauth2/refresh";
};
alias ServiceOAuth<T extends string[]> = OAuth2<[ServiceFlow], T>;
This simplify the reusable template you might have to create specially if you had multiple flows
|
|
Pros:
- Less verbose
Cons:
- Could be breaking depending on how we remove the
scopes
property from theFlow
(might be able to keep as backward compatible) - You now cannot have different scopes for different flows. I don't know of a use case for this but openapi3 was designed that way.
- Still not the cleansest at the operation level.
To build on the previous proposal we could add a new decorator @authScope
that would be able to specify the scopes separately from the auth.
The decorator would define the scopes for the auth scheme that would use a scope.
@useAuth(OAuth2<{
type: OAuth2FlowType.implicit;
authorizationUrl: "https://api.example.com/oauth2/authorize";
refreshUrl: "https://api.example.com/oauth2/refresh";
}>)
@authScopes("read", "write", "delete")
namespace MyService;
// Have to use X-API-KEY header auth.
@authScopes("read")
op list(): FileInfo[];
@authScopes("read")
op read(): FileInfo;
@authScopes("write")
op upload();
@authScopes("delete")
op delete();
Pros:
- Much cleaner
Cons:
- Still could be breaking same as previous alternative
- Scopes cannot be different for different auth scheme. Not sure if that's a real use case but it would be blocking in case it is.
Thanks for progressing this. Happy to leave the design to you, but FWIW I don't have a requirement to support different scopes in different auth schemes (which sounds like a nightmare).