mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-06 13:57:41 +00:00
fix: add expiration date information to service users keys (#7497)
* feat: add ExpirationDate to MachineKey JSON detail * fix: include time in expiration date column for machine keys table * fix: show expiration date in ShowKeyDialog if available * fix: add machine key expiration date note --------- Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
parent
30a1f4b39e
commit
dff5984f7d
@ -61,7 +61,7 @@
|
||||
<ng-container matColumnDef="expirationDate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let key">
|
||||
{{ key.expirationDate | timestampToDate | localizedDate: 'fromNow' }}
|
||||
{{ key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
@ -17,6 +17,13 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row" *ngIf="expirationDate">
|
||||
<p class="left cnsl-secondary-text">{{ 'USER.MACHINE.EXPIRATIONDATE' | translate }}</p>
|
||||
<p class="right">
|
||||
{{ expirationDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button class="download-button" mat-stroked-button color="primary" (click)="saveFile()">Download</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
@ -11,6 +11,7 @@ import { InfoSectionType } from '../info-section/info-section.component';
|
||||
})
|
||||
export class ShowKeyDialogComponent {
|
||||
public keyResponse!: AddMachineKeyResponse.AsObject | AddAppKeyResponse.AsObject;
|
||||
public expirationDate: string = '';
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
constructor(
|
||||
@ -18,6 +19,10 @@ export class ShowKeyDialogComponent {
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.keyResponse = data.key;
|
||||
if (this.keyResponse.keyDetails) {
|
||||
const keyDetails: { expirationDate: string } = JSON.parse(atob(this.keyResponse.keyDetails.toString()));
|
||||
this.expirationDate = keyDetails.expirationDate;
|
||||
}
|
||||
}
|
||||
|
||||
public saveFile(): void {
|
||||
|
@ -4,6 +4,7 @@ sidebar_label: Service Users
|
||||
---
|
||||
|
||||
This is a guide on how to create service users in ZITADEL. You can read more about users [here](/concepts/structure/users.md).
|
||||
|
||||
## Create a Service User
|
||||
|
||||
1. Navigate to Service Users
|
||||
@ -32,18 +33,22 @@ In this step we will authenticate a service user and receive an access_token to
|
||||
|
||||
### 1. Generate a private-public key pair in ZITADEL
|
||||
|
||||
Select your service user and in the section KEYS click **New**. Enter an expiration date and click **Add**. Make sure to download the json by clicking **Download**.
|
||||
Select your service user and in the section KEYS click **New**. Enter an optional expiration date and click **Add**. Make sure to download the json by clicking **Download**.
|
||||
|
||||
:::note
|
||||
If you specify an expiration date, note that the key will expire at midnight that day
|
||||
:::
|
||||
|
||||
![Create private key](/img/console_serviceusers_new_key.gif)
|
||||
|
||||
The downloaded json should look something like outlined below. The value of `key` contains the *private* key for your service account. Please make sure to keep this key securely stored and handle with care. The public key is automatically stored in ZITADEL.
|
||||
The downloaded json should look something like outlined below. The value of `key` contains the _private_ key for your service account. Please make sure to keep this key securely stored and handle with care. The public key is automatically stored in ZITADEL.
|
||||
|
||||
```json
|
||||
{
|
||||
"type":"serviceaccount",
|
||||
"keyId":"100509901696068329",
|
||||
"key":"-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----\n",
|
||||
"userId":"100507859606888466"
|
||||
"type": "serviceaccount",
|
||||
"keyId": "100509901696068329",
|
||||
"key": "-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----\n",
|
||||
"userId": "100507859606888466"
|
||||
}
|
||||
```
|
||||
|
||||
@ -55,8 +60,8 @@ Header
|
||||
|
||||
```json
|
||||
{
|
||||
"alg": "RS256",
|
||||
"kid":"100509901696068329"
|
||||
"alg": "RS256",
|
||||
"kid": "100509901696068329"
|
||||
}
|
||||
```
|
||||
|
||||
@ -74,11 +79,11 @@ Payload
|
||||
}
|
||||
```
|
||||
|
||||
* `iss` represents the requesting party, i.e. the owner of the private key. In this case the value of `userId` from the downloaded JSON.
|
||||
* `sub` represents the application. Set the value also to the value of `userId`
|
||||
* `aud` must be ZITADEL's issuing domain
|
||||
* `iat` is a unix timestamp of the creation signing time of the JWT, e.g. now and must not be older than 1 hour ago
|
||||
* `exp` is the unix timestamp of expiry of this assertion
|
||||
- `iss` represents the requesting party, i.e. the owner of the private key. In this case the value of `userId` from the downloaded JSON.
|
||||
- `sub` represents the application. Set the value also to the value of `userId`
|
||||
- `aud` must be ZITADEL's issuing domain
|
||||
- `iat` is a unix timestamp of the creation signing time of the JWT, e.g. now and must not be older than 1 hour ago
|
||||
- `exp` is the unix timestamp of expiry of this assertion
|
||||
|
||||
Please refer to [JWT_with_Private_Key](/apis/openidoauth/authn-methods#jwt-with-private-key) in the documentation for further information.
|
||||
|
||||
@ -99,11 +104,11 @@ curl --request POST \
|
||||
|
||||
If you want to access the ZITADEL API with this access token, you have to add `urn:zitadel:iam:org:project:id:zitadel:aud` to the list of scopes.
|
||||
|
||||
* `grant_type` should be set to `urn:ietf:params:oauth:grant-type:jwt-bearer`
|
||||
* `scope` should contain any [Scopes](/apis/openidoauth/scopes) you want to include, but must include `openid`. For this example, please include `profile` and `email`
|
||||
* `assertion` is the encoded value of the JWT that was signed with your private key from the prior step
|
||||
- `grant_type` should be set to `urn:ietf:params:oauth:grant-type:jwt-bearer`
|
||||
- `scope` should contain any [Scopes](/apis/openidoauth/scopes) you want to include, but must include `openid`. For this example, please include `profile` and `email`
|
||||
- `assertion` is the encoded value of the JWT that was signed with your private key from the prior step
|
||||
|
||||
You should receive a successful response with `access_token`, `token_type` and time to expiry in seconds as `expires_in`.
|
||||
You should receive a successful response with `access_token`, `token_type` and time to expiry in seconds as `expires_in`.
|
||||
|
||||
```bash
|
||||
HTTP/1.1 200 OK
|
||||
@ -142,11 +147,11 @@ Content-Type: application/json
|
||||
|
||||
## Summary
|
||||
|
||||
* With service users you can secure machine-to-machine communication
|
||||
* Because there is no interactive logon, you need to use a JWT signed with your private key to authorize the user
|
||||
* After successful authorization you can use an access token like for human users
|
||||
- With service users you can secure machine-to-machine communication
|
||||
- Because there is no interactive logon, you need to use a JWT signed with your private key to authorize the user
|
||||
- After successful authorization you can use an access token like for human users
|
||||
|
||||
Where to go from here:
|
||||
|
||||
* Management API
|
||||
* Securing backend API
|
||||
- Management API
|
||||
- Securing backend API
|
||||
|
@ -56,7 +56,7 @@ func (key *MachineKey) Detail() ([]byte, error) {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "KEY-sp2l2m", "Errors.Internal")
|
||||
}
|
||||
if key.Type == domain.AuthNKeyTypeJSON {
|
||||
return domain.MachineKeyMarshalJSON(key.KeyID, key.PrivateKey, key.AggregateID)
|
||||
return domain.MachineKeyMarshalJSON(key.KeyID, key.PrivateKey, key.ExpirationDate, key.AggregateID)
|
||||
}
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "KEY-dsg52", "Errors.Internal")
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func (key *MachineKey) Detail() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (key *MachineKey) MarshalJSON() ([]byte, error) {
|
||||
return MachineKeyMarshalJSON(key.KeyID, key.PrivateKey, key.AggregateID)
|
||||
return MachineKeyMarshalJSON(key.KeyID, key.PrivateKey, key.ExpirationDate, key.AggregateID)
|
||||
}
|
||||
|
||||
type MachineKeyState int32
|
||||
@ -59,16 +59,18 @@ func (f MachineKeyState) Valid() bool {
|
||||
return f >= 0 && f < machineKeyStateCount
|
||||
}
|
||||
|
||||
func MachineKeyMarshalJSON(keyID string, privateKey []byte, userID string) ([]byte, error) {
|
||||
func MachineKeyMarshalJSON(keyID string, privateKey []byte, expirationDate time.Time, userID string) ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Type string `json:"type"`
|
||||
KeyID string `json:"keyId"`
|
||||
Key string `json:"key"`
|
||||
UserID string `json:"userId"`
|
||||
Type string `json:"type"`
|
||||
KeyID string `json:"keyId"`
|
||||
Key string `json:"key"`
|
||||
ExpirationDate time.Time `json:"expirationDate"`
|
||||
UserID string `json:"userId"`
|
||||
}{
|
||||
Type: "serviceaccount",
|
||||
KeyID: keyID,
|
||||
Key: string(privateKey),
|
||||
UserID: userID,
|
||||
Type: "serviceaccount",
|
||||
KeyID: keyID,
|
||||
Key: string(privateKey),
|
||||
ExpirationDate: expirationDate,
|
||||
UserID: userID,
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user