MediaWiki master
generateJwt.php
Go to the documentation of this file.
1<?php
12
13// @codeCoverageIgnoreStart
14require_once __DIR__ . '/Maintenance.php';
15// @codeCoverageIgnoreEnd
16
23class GenerateJwt extends Maintenance {
24
25 public function __construct() {
26 parent::__construct();
27 $this->addDescription( 'Generate a JWT token from a JSON file or a JSON string' );
28 $this->addOption( 'file', 'A filename with claims stored as JSON', false, true );
29 $this->addOption( 'json', 'A json string with claims', false, true );
30 $this->addOption( 'include-default-claims', 'Inject default claims: iss, iat, jti and sxp', false );
31 $this->addOption( 'validate', 'Validate if JWT has all required fields (iss, sub)', false );
32 $this->addOption( 'verbose', 'Be verbose and output the claims array', false );
33 }
34
40 protected function getDefaultClaims() {
41 return [
42 'iss' => $this->getServiceContainer()->getUrlUtils()->getCanonicalServer(),
43 'iat' => MWTimestamp::time(),
44 'jti' => base64_encode( random_bytes( 16 ) ),
45 'sxp' => MWTimestamp::time() + 3600,
46 ];
47 }
48
55 private function validateClaims( array $claims ) {
56 foreach ( [ 'iss', 'sub' ] as $requiredClaim ) {
57 if ( !array_key_exists( $requiredClaim, $claims ) ) {
58 $this->fatalError( 'Missing required claim: ' . $requiredClaim );
59 }
60 }
61 }
62
68 private function readClaimsFromInput(): array {
69 $file = $this->getOption( 'file' );
70 $json = $this->getOption( 'json' );
71 if ( $file === null && $json === null ) {
72 $this->fatalError( 'Either --file or --json must be specified' );
73 }
74 if ( $file ) {
75 if ( !file_exists( $file ) ) {
76 $this->fatalError( 'File does not exist: ' . $file );
77 }
78 $this->output( 'Reading claims from file: ' . $file . PHP_EOL );
79 $content = file_get_contents( $file );
80 } else {
81 $content = $json;
82 }
83 if ( strlen( $content ) == 0 ) {
84 $this->fatalError( 'Empty content, cannot decode' );
85 }
86 $claims = json_decode( $content, true );
87 $lastError = json_last_error();
88 if ( $lastError !== JSON_ERROR_NONE ) {
89 $this->fatalError( 'Invalid JSON: ' . json_last_error_msg() );
90 }
91 if ( !is_array( $claims ) ) {
92 $this->fatalError( 'Decoded claims structure is not an array' );
93 }
94 return $claims;
95 }
96
97 public function execute() {
98 $jwtCodec = $this->getServiceContainer()->getJwtCodec();
99 if ( !$jwtCodec->isEnabled() ) {
100 $this->fatalError( 'JWT is not enabled on this wiki. Please setup JwtPublicKey and JwtPrivateKey' );
101 }
102
103 $claims = $this->readClaimsFromInput();
104
105 if ( $this->hasOption( 'include-default-claims' ) ) {
106 $claims = $this->getDefaultClaims() + $claims;
107 }
108
109 if ( $this->getOption( 'verbose' ) ) {
110 $this->output( 'Decoded Claims: ' . PHP_EOL );
111 $this->output( json_encode( $claims, JSON_PRETTY_PRINT ) . PHP_EOL );
112 }
113
114 if ( $this->getOption( 'validate' ) ) {
115 $this->validateClaims( $claims );
116 }
117
118 $token = $jwtCodec->create( $claims );
119 $this->output( $token . PHP_EOL );
120 }
121}
122
123// @codeCoverageIgnoreStart
124$maintClass = GenerateJWT::class;
125require_once RUN_MAINTENANCE_IF_MAIN;
126// @codeCoverageIgnoreEnd
Maintenance script to generate a JWT token.
__construct()
Default constructor.
execute()
Do the actual work.
getDefaultClaims()
Retrieve default claims to inject into the JWT token.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
output( $out, $channel=null)
Throw some output to the user.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
getServiceContainer()
Returns the main service container.
addDescription( $text)
Set the description text.
Library for creating and parsing MW-style timestamps.
$maintClass